Completed
Branch master (c3ca5f)
by Seth
04:51
created
phpicalendar/lib/HTTP/CalDAV/Tools/ReportParser.php 1 patch
Indentation   +226 added lines, -226 removed lines patch added patch discarded remove patch
@@ -41,232 +41,232 @@
 block discarded – undo
41 41
  */
42 42
 class ReportParser
43 43
 {
44
-    /**
45
-     * Success state flag
46
-     *
47
-     * @var bool
48
-     * @access public
49
-     */
50
-    var $success = false;
51
-
52
-    /**
53
-     * Name of the requested report
54
-     *
55
-     * @var string
56
-     * @access public
57
-     */
58
-    var $report;
59
-
60
-    /**
61
-     * Found properties are collected here
62
-     *
63
-     * @var array
64
-     * @access public
65
-     */
66
-    var $props = array();
67
-
68
-    /**
69
-     * Found filters are collected here
70
-     *
71
-     * @var array
72
-     * @access public
73
-     */
74
-    var $filters = array();
75
-
76
-    /**
77
-     * Stack of ancestor tag names
78
-     *
79
-     * @var array
80
-     * @access private
81
-     */
82
-    var $_names = array();
83
-
84
-    /**
85
-     * Stack of component data
86
-     *
87
-     * @var array
88
-     * @access private
89
-     */
90
-    var $_comps = array();
91
-
92
-    /**
93
-     * Constructor
94
-     *
95
-     * @param string path to report input data
96
-     * @access public
97
-     */
98
-    function ReportParser($input)
99
-    {
100
-        // FIXME Take a handle, not a path
101
-        $handle = fopen($input, 'r');
102
-        if (!$handle) {
103
-            return;
104
-        }
105
-
106
-        $parser = xml_parser_create_ns('UTF-8', ' ');
107
-        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
108
-        xml_set_element_handler($parser, array(&$this, '_startElement'),
109
-            array(&$this, '_endElement'));
110
-
111
-        $this->success = true;
112
-        while (($line = fgets($handle, 4096)) !== false) {
113
-            $this->success = xml_parse($parser, $line);
114
-            if (!$this->success) {
115
-                return;
116
-            }
117
-        }
118
-
119
-        if (!feof($handle)) {
120
-            $this->success = false;
121
-            return;
122
-        }
123
-
124
-        xml_parser_free($parser);
125
-        fclose($handle);
126
-
127
-        if (empty($this->props)) {
128
-            $this->props = 'allprop';
129
-        }
130
-    }
131
-
132
-    /**
133
-     * Start tag handler
134
-     *
135
-     * @param object parser
136
-     * @param string tag name
137
-     * @param array tag attributes
138
-     * @access private
139
-     */
140
-    function _startElement($parser, $name, $attrs)
141
-    {
142
-        $nameComponents = explode(' ', $name);
143
-        if (count($nameComponents) > 2) {
144
-            $this->success = false;
145
-            return;
146
-        }
147
-
148
-        if (count($nameComponents) == 2) {
149
-            list ($ns, $name) = $nameComponents;
150
-            if (empty($ns)) {
151
-                $this->success = false;
152
-                return;
153
-            }
154
-        }
155
-
156
-        if (empty($this->_names)) {
157
-            $this->report = $name;
158
-            $this->_names[] = $name;
159
-            return;
160
-        }
161
-
162
-        if (count($this->_names) == 1 &&
163
-                ($name == 'allprop' || $name == 'propname')) {
164
-            $this->props = $name;
165
-            $this->_names[] = $name;
166
-            return;
167
-        }
168
-
169
-        if (count($this->_names) == 2 && end($this->_names) == 'prop') {
170
-            $prop = array('name' => $name, 'ns' => $ns);
171
-
172
-            if ($name == 'calendar-data') {
173
-                $prop['value'] = array();
174
-                $this->_comps[] =& $prop['value'];
175
-            }
176
-
177
-            $this->props[] = $prop;
178
-            $this->_names[] = $name;
179
-            return;
180
-        }
181
-
182
-        if ($name == 'comp') {
183
-            if (!is_array($this->_comps[count($this->_comps) - 1]['comps'])) {
184
-                $this->_comps[count($this->_comps) - 1]['comps'] = array();
185
-            }
186
-
187
-            $this->_comps[count($this->_comps) - 1]['comps'][$attrs['name']] =
188
-                array();
189
-            $this->_comps[] =& $this->_comps[count($this->_comps) - 1]['comps']
190
-                [$attrs['name']];
191
-            $this->_names[] = $name;
192
-            return;
193
-        }
194
-
195
-        if (end($this->_names) == 'comp' && $name == 'prop') {
196
-            if (!is_array($this->_comps[count($this->_comps) - 1]['props'])) {
197
-                $this->_comps[count($this->_comps) - 1]['props'] = array();
198
-            }
199
-
200
-            $this->_comps[count($this->_comps) - 1]['props'][] = $attrs['name'];
201
-            $this->_names[] = $name;
202
-            return;
203
-        }
204
-
205
-        if (count($this->_names) == 1 && $name == 'filter') {
206
-            $this->_comps[] =& $this->filters;
207
-            $this->_names[] = $name;
208
-            return;
209
-        }
210
-
211
-        if ($name == 'comp-filter') {
212
-            if (!is_array($this->_comps[count($this->_comps) - 1]['comps'])) {
213
-                $this->_comps[count($this->_comps) - 1]['comps'] = array();
214
-            }
215
-
216
-            $this->_comps[count($this->_comps) - 1]['comps'][$attrs['name']] =
217
-                array();
218
-            $this->_comps[] =& $this->_comps[count($this->_comps) - 1]['comps']
219
-                [$attrs['name']];
220
-            $this->_names[] = $name;
221
-            return;
222
-        }
223
-
224
-        if (end($this->_names) == 'comp-filter') {
225
-            if (!is_array($this->_comps[count($this->_comps) - 1]['filters'])) {
226
-                $this->_comps[count($this->_comps) - 1]['filters'] = array();
227
-            }
228
-
229
-            $this->_comps[count($this->_comps) - 1]['filters'][] =
230
-                array('name' => $name, 'value' => $attrs);
231
-            $this->_names[] = $name;
232
-            return;
233
-        }
234
-
235
-        $this->_names[] = $name;
236
-    }
237
-
238
-    /**
239
-     * End tag handler
240
-     *
241
-     * @param object parser
242
-     * @param string tag name
243
-     * @param array tag attributes
244
-     * @access private
245
-     */
246
-    function _endElement($parser, $name) {
247
-        $nameComponents = explode(' ', $name);
248
-        if (count($nameComponents) > 2) {
249
-            $this->success = false;
250
-            return;
251
-        }
252
-
253
-        if (count($nameComponents) == 2) {
254
-            list ($ns, $name) = $nameComponents;
255
-            if (empty($ns)) {
256
-                $this->success = false;
257
-                return;
258
-            }
259
-        }
260
-
261
-        // Any need to pop at end of calendar-data?
262
-        // Yes - $this->_comps is re-used for parsing filters
263
-        if ($name == 'comp' || $name == 'calendar-data' ||
264
-                $name == 'comp-filter' || $name == 'filter') {
265
-            array_pop($this->_comps);
266
-        }
267
-
268
-        array_pop($this->_names);
269
-    }
44
+	/**
45
+	 * Success state flag
46
+	 *
47
+	 * @var bool
48
+	 * @access public
49
+	 */
50
+	var $success = false;
51
+
52
+	/**
53
+	 * Name of the requested report
54
+	 *
55
+	 * @var string
56
+	 * @access public
57
+	 */
58
+	var $report;
59
+
60
+	/**
61
+	 * Found properties are collected here
62
+	 *
63
+	 * @var array
64
+	 * @access public
65
+	 */
66
+	var $props = array();
67
+
68
+	/**
69
+	 * Found filters are collected here
70
+	 *
71
+	 * @var array
72
+	 * @access public
73
+	 */
74
+	var $filters = array();
75
+
76
+	/**
77
+	 * Stack of ancestor tag names
78
+	 *
79
+	 * @var array
80
+	 * @access private
81
+	 */
82
+	var $_names = array();
83
+
84
+	/**
85
+	 * Stack of component data
86
+	 *
87
+	 * @var array
88
+	 * @access private
89
+	 */
90
+	var $_comps = array();
91
+
92
+	/**
93
+	 * Constructor
94
+	 *
95
+	 * @param string path to report input data
96
+	 * @access public
97
+	 */
98
+	function ReportParser($input)
99
+	{
100
+		// FIXME Take a handle, not a path
101
+		$handle = fopen($input, 'r');
102
+		if (!$handle) {
103
+			return;
104
+		}
105
+
106
+		$parser = xml_parser_create_ns('UTF-8', ' ');
107
+		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
108
+		xml_set_element_handler($parser, array(&$this, '_startElement'),
109
+			array(&$this, '_endElement'));
110
+
111
+		$this->success = true;
112
+		while (($line = fgets($handle, 4096)) !== false) {
113
+			$this->success = xml_parse($parser, $line);
114
+			if (!$this->success) {
115
+				return;
116
+			}
117
+		}
118
+
119
+		if (!feof($handle)) {
120
+			$this->success = false;
121
+			return;
122
+		}
123
+
124
+		xml_parser_free($parser);
125
+		fclose($handle);
126
+
127
+		if (empty($this->props)) {
128
+			$this->props = 'allprop';
129
+		}
130
+	}
131
+
132
+	/**
133
+	 * Start tag handler
134
+	 *
135
+	 * @param object parser
136
+	 * @param string tag name
137
+	 * @param array tag attributes
138
+	 * @access private
139
+	 */
140
+	function _startElement($parser, $name, $attrs)
141
+	{
142
+		$nameComponents = explode(' ', $name);
143
+		if (count($nameComponents) > 2) {
144
+			$this->success = false;
145
+			return;
146
+		}
147
+
148
+		if (count($nameComponents) == 2) {
149
+			list ($ns, $name) = $nameComponents;
150
+			if (empty($ns)) {
151
+				$this->success = false;
152
+				return;
153
+			}
154
+		}
155
+
156
+		if (empty($this->_names)) {
157
+			$this->report = $name;
158
+			$this->_names[] = $name;
159
+			return;
160
+		}
161
+
162
+		if (count($this->_names) == 1 &&
163
+				($name == 'allprop' || $name == 'propname')) {
164
+			$this->props = $name;
165
+			$this->_names[] = $name;
166
+			return;
167
+		}
168
+
169
+		if (count($this->_names) == 2 && end($this->_names) == 'prop') {
170
+			$prop = array('name' => $name, 'ns' => $ns);
171
+
172
+			if ($name == 'calendar-data') {
173
+				$prop['value'] = array();
174
+				$this->_comps[] =& $prop['value'];
175
+			}
176
+
177
+			$this->props[] = $prop;
178
+			$this->_names[] = $name;
179
+			return;
180
+		}
181
+
182
+		if ($name == 'comp') {
183
+			if (!is_array($this->_comps[count($this->_comps) - 1]['comps'])) {
184
+				$this->_comps[count($this->_comps) - 1]['comps'] = array();
185
+			}
186
+
187
+			$this->_comps[count($this->_comps) - 1]['comps'][$attrs['name']] =
188
+				array();
189
+			$this->_comps[] =& $this->_comps[count($this->_comps) - 1]['comps']
190
+				[$attrs['name']];
191
+			$this->_names[] = $name;
192
+			return;
193
+		}
194
+
195
+		if (end($this->_names) == 'comp' && $name == 'prop') {
196
+			if (!is_array($this->_comps[count($this->_comps) - 1]['props'])) {
197
+				$this->_comps[count($this->_comps) - 1]['props'] = array();
198
+			}
199
+
200
+			$this->_comps[count($this->_comps) - 1]['props'][] = $attrs['name'];
201
+			$this->_names[] = $name;
202
+			return;
203
+		}
204
+
205
+		if (count($this->_names) == 1 && $name == 'filter') {
206
+			$this->_comps[] =& $this->filters;
207
+			$this->_names[] = $name;
208
+			return;
209
+		}
210
+
211
+		if ($name == 'comp-filter') {
212
+			if (!is_array($this->_comps[count($this->_comps) - 1]['comps'])) {
213
+				$this->_comps[count($this->_comps) - 1]['comps'] = array();
214
+			}
215
+
216
+			$this->_comps[count($this->_comps) - 1]['comps'][$attrs['name']] =
217
+				array();
218
+			$this->_comps[] =& $this->_comps[count($this->_comps) - 1]['comps']
219
+				[$attrs['name']];
220
+			$this->_names[] = $name;
221
+			return;
222
+		}
223
+
224
+		if (end($this->_names) == 'comp-filter') {
225
+			if (!is_array($this->_comps[count($this->_comps) - 1]['filters'])) {
226
+				$this->_comps[count($this->_comps) - 1]['filters'] = array();
227
+			}
228
+
229
+			$this->_comps[count($this->_comps) - 1]['filters'][] =
230
+				array('name' => $name, 'value' => $attrs);
231
+			$this->_names[] = $name;
232
+			return;
233
+		}
234
+
235
+		$this->_names[] = $name;
236
+	}
237
+
238
+	/**
239
+	 * End tag handler
240
+	 *
241
+	 * @param object parser
242
+	 * @param string tag name
243
+	 * @param array tag attributes
244
+	 * @access private
245
+	 */
246
+	function _endElement($parser, $name) {
247
+		$nameComponents = explode(' ', $name);
248
+		if (count($nameComponents) > 2) {
249
+			$this->success = false;
250
+			return;
251
+		}
252
+
253
+		if (count($nameComponents) == 2) {
254
+			list ($ns, $name) = $nameComponents;
255
+			if (empty($ns)) {
256
+				$this->success = false;
257
+				return;
258
+			}
259
+		}
260
+
261
+		// Any need to pop at end of calendar-data?
262
+		// Yes - $this->_comps is re-used for parsing filters
263
+		if ($name == 'comp' || $name == 'calendar-data' ||
264
+				$name == 'comp-filter' || $name == 'filter') {
265
+			array_pop($this->_comps);
266
+		}
267
+
268
+		array_pop($this->_names);
269
+	}
270 270
 }
271 271
 
272 272
 /*
Please login to merge, or discard this patch.
phpicalendar/lib/HTTP/WebDAV/Server.php 1 patch
Indentation   +2255 added lines, -2255 removed lines patch added patch discarded remove patch
@@ -33,2266 +33,2266 @@  discard block
 block discarded – undo
33 33
  */
34 34
 class HTTP_WebDAV_Server
35 35
 {
36
-    // {{{ Member Variables
37
-
38
-    /**
39
-     * URI path for this request
40
-     *
41
-     * @var string
42
-     */
43
-    var $path;
44
-
45
-    /**
46
-     * base URI for this request
47
-     *
48
-     * @var string
49
-     */
50
-    var $base_uri;
51
-
52
-    /**
53
-     * Realm string to be used in authentification popups
54
-     *
55
-     * @var string
56
-     */
57
-    var $http_auth_realm = 'PHP WebDAV';
58
-
59
-    /**
60
-     * String to be used in "X-Dav-Powered-By" header
61
-     *
62
-     * @var string
63
-     */
64
-    var $dav_powered_by = '';
65
-
66
-    /**
67
-     * Remember parsed If: (RFC2518 9.4) header conditions
68
-     *
69
-     * @var array
70
-     */
71
-    var $_if_header_uris = array();
72
-
73
-    /**
74
-     * HTTP response status/message
75
-     *
76
-     * @var string
77
-     */
78
-    var $_http_status = '200 OK';
79
-
80
-    /**
81
-     * encoding of property values passed in
82
-     *
83
-     * @var string
84
-     */
85
-    var $_prop_encoding = 'utf-8';
86
-
87
-    // }}}
88
-
89
-    // {{{ ServeRequest
90
-
91
-    /**
92
-     * Serve WebDAV HTTP request
93
-     *
94
-     * dispatch WebDAV HTTP request to the apropriate method wrapper
95
-     *
96
-     * @param void
97
-     * @return void
98
-     */
99
-    function ServeRequest()
100
-    {
101
-        // identify ourselves
102
-        if (empty($this->dav_powered_by)) {
103
-            $this->dav_powered_by = 'PHP class: ' . get_class($this);
104
-        }
105
-        header('X-Dav-Powered-By: ' . $this->dav_powered_by);
106
-
107
-        // set path
108
-        if (empty($this->path)) {
109
-            $this->path = $this->_urldecode($_SERVER['PATH_INFO']);
110
-            $this->path = ltrim($this->path, '/');
111
-            $this->path = rtrim($this->path, '/');
112
-        }
113
-
114
-        if (ini_get('magic_quotes_gpc')) {
115
-            $this->path = stripslashes($this->path);
116
-        }
117
-
118
-        // set base uri
119
-        if (empty($this->base_uri)) {
120
-            $path_info = $this->_urldecode($_SERVER['PATH_INFO']);
121
-            $request_uri = $this->_urldecode($_SERVER['REQUEST_URI']);
122
-            $this->base_uri = substr($request_uri, 0, strlen($request_uri) -
123
-                strlen($path_info));
124
-            $this->base_uri = rtrim($this->base_uri, '/');
125
-        }
126
-
127
-        // check authentication
128
-        if (!$this->check_auth_wrapper()) {
129
-
130
-            // RFC2518 says we must use Digest instead of Basic
131
-            // but Microsoft Clients do not support Digest
132
-            // and we don't support NTLM or Kerberos
133
-            // so we are stuck with Basic here
134
-            header('WWW-Authenticate: Basic realm="' . ($this->http_auth_realm) . '"');
135
-
136
-            // Windows seems to require this being the last header sent
137
-            // (changed according to PECL bug #3138)
138
-            $this->http_status('401 Authentication Required');
139
-
140
-            return;
141
-        }
142
-
143
-        // check
144
-        if (! $this->_check_if_header_conditions()) {
145
-            $this->http_status('412 Precondition Failed');
146
-            return;
147
-        }
148
-
149
-        // detect requested method names
150
-        $method = strtolower($_SERVER['REQUEST_METHOD']);
151
-        $wrapper = $method . '_wrapper';
152
-
153
-        // emulate HEAD using GET if no HEAD method found
154
-        if ($wrapper == 'head_wrapper' &&
155
-                !method_exists($this, 'head')) {
156
-            $method = 'get';
157
-        }
158
-
159
-        if (method_exists($this, $method) &&
160
-                method_exists($this, $wrapper) ||
161
-                $method == 'options') {
162
-            $this->$wrapper();
163
-            return;
164
-        }
165
-
166
-        // method not found/implemented
167
-        if ($method == 'lock') {
168
-            $this->http_status('412 Precondition Failed');
169
-            return;
170
-        }
171
-
172
-        // tell client what's allowed
173
-        header('Allow: ' . implode(', ', $this->_allow()));
174
-        $this->http_status('405 Method Not Allowed');
175
-    }
176
-
177
-    // }}}
178
-
179
-    // {{{ abstract WebDAV methods
180
-
181
-    // {{{ GET
182
-
183
-    /**
184
-     * GET implementation
185
-     *
186
-     * overload this method to retrieve resources from your server
187
-     * <br>
188
-     *
189
-     *
190
-     * @abstract
191
-     * @param array &$params array of input and output parameters
192
-     * <br><b>input</b><ul>
193
-     * <li> path -
194
-     * </ul>
195
-     * <br><b>output</b><ul>
196
-     * <li> size -
197
-     * </ul>
198
-     * @returns int HTTP-Statuscode
199
-     */
200
-
201
-    /* abstract
36
+	// {{{ Member Variables
37
+
38
+	/**
39
+	 * URI path for this request
40
+	 *
41
+	 * @var string
42
+	 */
43
+	var $path;
44
+
45
+	/**
46
+	 * base URI for this request
47
+	 *
48
+	 * @var string
49
+	 */
50
+	var $base_uri;
51
+
52
+	/**
53
+	 * Realm string to be used in authentification popups
54
+	 *
55
+	 * @var string
56
+	 */
57
+	var $http_auth_realm = 'PHP WebDAV';
58
+
59
+	/**
60
+	 * String to be used in "X-Dav-Powered-By" header
61
+	 *
62
+	 * @var string
63
+	 */
64
+	var $dav_powered_by = '';
65
+
66
+	/**
67
+	 * Remember parsed If: (RFC2518 9.4) header conditions
68
+	 *
69
+	 * @var array
70
+	 */
71
+	var $_if_header_uris = array();
72
+
73
+	/**
74
+	 * HTTP response status/message
75
+	 *
76
+	 * @var string
77
+	 */
78
+	var $_http_status = '200 OK';
79
+
80
+	/**
81
+	 * encoding of property values passed in
82
+	 *
83
+	 * @var string
84
+	 */
85
+	var $_prop_encoding = 'utf-8';
86
+
87
+	// }}}
88
+
89
+	// {{{ ServeRequest
90
+
91
+	/**
92
+	 * Serve WebDAV HTTP request
93
+	 *
94
+	 * dispatch WebDAV HTTP request to the apropriate method wrapper
95
+	 *
96
+	 * @param void
97
+	 * @return void
98
+	 */
99
+	function ServeRequest()
100
+	{
101
+		// identify ourselves
102
+		if (empty($this->dav_powered_by)) {
103
+			$this->dav_powered_by = 'PHP class: ' . get_class($this);
104
+		}
105
+		header('X-Dav-Powered-By: ' . $this->dav_powered_by);
106
+
107
+		// set path
108
+		if (empty($this->path)) {
109
+			$this->path = $this->_urldecode($_SERVER['PATH_INFO']);
110
+			$this->path = ltrim($this->path, '/');
111
+			$this->path = rtrim($this->path, '/');
112
+		}
113
+
114
+		if (ini_get('magic_quotes_gpc')) {
115
+			$this->path = stripslashes($this->path);
116
+		}
117
+
118
+		// set base uri
119
+		if (empty($this->base_uri)) {
120
+			$path_info = $this->_urldecode($_SERVER['PATH_INFO']);
121
+			$request_uri = $this->_urldecode($_SERVER['REQUEST_URI']);
122
+			$this->base_uri = substr($request_uri, 0, strlen($request_uri) -
123
+				strlen($path_info));
124
+			$this->base_uri = rtrim($this->base_uri, '/');
125
+		}
126
+
127
+		// check authentication
128
+		if (!$this->check_auth_wrapper()) {
129
+
130
+			// RFC2518 says we must use Digest instead of Basic
131
+			// but Microsoft Clients do not support Digest
132
+			// and we don't support NTLM or Kerberos
133
+			// so we are stuck with Basic here
134
+			header('WWW-Authenticate: Basic realm="' . ($this->http_auth_realm) . '"');
135
+
136
+			// Windows seems to require this being the last header sent
137
+			// (changed according to PECL bug #3138)
138
+			$this->http_status('401 Authentication Required');
139
+
140
+			return;
141
+		}
142
+
143
+		// check
144
+		if (! $this->_check_if_header_conditions()) {
145
+			$this->http_status('412 Precondition Failed');
146
+			return;
147
+		}
148
+
149
+		// detect requested method names
150
+		$method = strtolower($_SERVER['REQUEST_METHOD']);
151
+		$wrapper = $method . '_wrapper';
152
+
153
+		// emulate HEAD using GET if no HEAD method found
154
+		if ($wrapper == 'head_wrapper' &&
155
+				!method_exists($this, 'head')) {
156
+			$method = 'get';
157
+		}
158
+
159
+		if (method_exists($this, $method) &&
160
+				method_exists($this, $wrapper) ||
161
+				$method == 'options') {
162
+			$this->$wrapper();
163
+			return;
164
+		}
165
+
166
+		// method not found/implemented
167
+		if ($method == 'lock') {
168
+			$this->http_status('412 Precondition Failed');
169
+			return;
170
+		}
171
+
172
+		// tell client what's allowed
173
+		header('Allow: ' . implode(', ', $this->_allow()));
174
+		$this->http_status('405 Method Not Allowed');
175
+	}
176
+
177
+	// }}}
178
+
179
+	// {{{ abstract WebDAV methods
180
+
181
+	// {{{ GET
182
+
183
+	/**
184
+	 * GET implementation
185
+	 *
186
+	 * overload this method to retrieve resources from your server
187
+	 * <br>
188
+	 *
189
+	 *
190
+	 * @abstract
191
+	 * @param array &$params array of input and output parameters
192
+	 * <br><b>input</b><ul>
193
+	 * <li> path -
194
+	 * </ul>
195
+	 * <br><b>output</b><ul>
196
+	 * <li> size -
197
+	 * </ul>
198
+	 * @returns int HTTP-Statuscode
199
+	 */
200
+
201
+	/* abstract
202 202
        function GET()
203 203
        {
204 204
            // dummy entry for PHPDoc
205 205
        }
206 206
     */
207 207
 
208
-    // }}}
208
+	// }}}
209 209
 
210
-    // {{{ PUT
210
+	// {{{ PUT
211 211
 
212
-    /**
213
-     * PUT implementation
214
-     *
215
-     * @abstract
216
-     * @param array &$params
217
-     * @returns int HTTP-Statuscode
218
-     */
212
+	/**
213
+	 * PUT implementation
214
+	 *
215
+	 * @abstract
216
+	 * @param array &$params
217
+	 * @returns int HTTP-Statuscode
218
+	 */
219 219
 
220
-    /* abstract
220
+	/* abstract
221 221
        function PUT()
222 222
        {
223 223
            // dummy entry for PHPDoc
224 224
        }
225 225
     */
226 226
 
227
-    // }}}
227
+	// }}}
228 228
 
229
-    // {{{ COPY
229
+	// {{{ COPY
230 230
 
231
-    /**
232
-     * COPY implementation
233
-     *
234
-     * @abstract
235
-     * @param array &$params
236
-     * @returns int HTTP-Statuscode
237
-     */
231
+	/**
232
+	 * COPY implementation
233
+	 *
234
+	 * @abstract
235
+	 * @param array &$params
236
+	 * @returns int HTTP-Statuscode
237
+	 */
238 238
 
239
-    /* abstract
239
+	/* abstract
240 240
        function COPY()
241 241
        {
242 242
            // dummy entry for PHPDoc
243 243
        }
244 244
     */
245 245
 
246
-    // }}}
246
+	// }}}
247 247
 
248
-    // {{{ MOVE
248
+	// {{{ MOVE
249 249
 
250
-    /**
251
-     * MOVE implementation
252
-     *
253
-     * @abstract
254
-     * @param array &$params
255
-     * @returns int HTTP-Statuscode
256
-     */
250
+	/**
251
+	 * MOVE implementation
252
+	 *
253
+	 * @abstract
254
+	 * @param array &$params
255
+	 * @returns int HTTP-Statuscode
256
+	 */
257 257
 
258
-    /* abstract
258
+	/* abstract
259 259
        function MOVE()
260 260
        {
261 261
            // dummy entry for PHPDoc
262 262
        }
263 263
     */
264 264
 
265
-    // }}}
265
+	// }}}
266 266
 
267
-    // {{{ DELETE
267
+	// {{{ DELETE
268 268
 
269
-    /**
270
-     * DELETE implementation
271
-     *
272
-     * @abstract
273
-     * @param array &$params
274
-     * @returns int HTTP-Statuscode
275
-     */
269
+	/**
270
+	 * DELETE implementation
271
+	 *
272
+	 * @abstract
273
+	 * @param array &$params
274
+	 * @returns int HTTP-Statuscode
275
+	 */
276 276
 
277
-    /* abstract
277
+	/* abstract
278 278
        function DELETE()
279 279
        {
280 280
            // dummy entry for PHPDoc
281 281
        }
282 282
     */
283 283
 
284
-    // }}}
284
+	// }}}
285 285
 
286
-    // {{{ PROPFIND
286
+	// {{{ PROPFIND
287 287
 
288
-    /**
289
-     * PROPFIND implementation
290
-     *
291
-     * @abstract
292
-     * @param array &$params
293
-     * @returns int HTTP-Statuscode
294
-     */
288
+	/**
289
+	 * PROPFIND implementation
290
+	 *
291
+	 * @abstract
292
+	 * @param array &$params
293
+	 * @returns int HTTP-Statuscode
294
+	 */
295 295
 
296
-    /* abstract
296
+	/* abstract
297 297
        function PROPFIND()
298 298
        {
299 299
            // dummy entry for PHPDoc
300 300
        }
301 301
     */
302 302
 
303
-    // }}}
303
+	// }}}
304 304
 
305
-    // {{{ PROPPATCH
305
+	// {{{ PROPPATCH
306 306
 
307
-    /**
308
-     * PROPPATCH implementation
309
-     *
310
-     * @abstract
311
-     * @param array &$params
312
-     * @returns int HTTP-Statuscode
313
-     */
307
+	/**
308
+	 * PROPPATCH implementation
309
+	 *
310
+	 * @abstract
311
+	 * @param array &$params
312
+	 * @returns int HTTP-Statuscode
313
+	 */
314 314
 
315
-    /* abstract
315
+	/* abstract
316 316
        function PROPPATCH()
317 317
        {
318 318
            // dummy entry for PHPDoc
319 319
        }
320 320
     */
321 321
 
322
-    // }}}
322
+	// }}}
323 323
 
324
-    // {{{ LOCK
324
+	// {{{ LOCK
325 325
 
326
-    /**
327
-     * LOCK implementation
328
-     *
329
-     * @abstract
330
-     * @param array &$params
331
-     * @returns int HTTP-Statuscode
332
-     */
326
+	/**
327
+	 * LOCK implementation
328
+	 *
329
+	 * @abstract
330
+	 * @param array &$params
331
+	 * @returns int HTTP-Statuscode
332
+	 */
333 333
 
334
-    /* abstract
334
+	/* abstract
335 335
        function LOCK()
336 336
        {
337 337
            // dummy entry for PHPDoc
338 338
        }
339 339
     */
340 340
 
341
-    // }}}
341
+	// }}}
342 342
 
343
-    // {{{ UNLOCK
343
+	// {{{ UNLOCK
344 344
 
345
-    /**
346
-     * UNLOCK implementation
347
-     *
348
-     * @abstract
349
-     * @param array &$params
350
-     * @returns int HTTP-Statuscode
351
-     */
345
+	/**
346
+	 * UNLOCK implementation
347
+	 *
348
+	 * @abstract
349
+	 * @param array &$params
350
+	 * @returns int HTTP-Statuscode
351
+	 */
352 352
 
353
-    /* abstract
353
+	/* abstract
354 354
        function UNLOCK()
355 355
        {
356 356
            // dummy entry for PHPDoc
357 357
        }
358 358
     */
359 359
 
360
-    // }}}
360
+	// }}}
361 361
 
362
-    // }}}
362
+	// }}}
363 363
 
364
-    // {{{ other abstract methods
364
+	// {{{ other abstract methods
365 365
 
366
-    // {{{ checkAuth
366
+	// {{{ checkAuth
367 367
 
368
-    /**
369
-     * check authentication
370
-     *
371
-     * overload this method to retrieve and confirm authentication information
372
-     *
373
-     * @abstract
374
-     * @param string type Authentication type, e.g. "basic" or "digest"
375
-     * @param string username Transmitted username
376
-     * @param string passwort Transmitted password
377
-     * @returns bool Authentication status
378
-     */
368
+	/**
369
+	 * check authentication
370
+	 *
371
+	 * overload this method to retrieve and confirm authentication information
372
+	 *
373
+	 * @abstract
374
+	 * @param string type Authentication type, e.g. "basic" or "digest"
375
+	 * @param string username Transmitted username
376
+	 * @param string passwort Transmitted password
377
+	 * @returns bool Authentication status
378
+	 */
379 379
 
380
-    /* abstract
380
+	/* abstract
381 381
        function checkAuth($type, $username, $password)
382 382
        {
383 383
            // dummy entry for PHPDoc
384 384
        }
385 385
     */
386 386
 
387
-    // }}}
387
+	// }}}
388 388
 
389
-    // {{{ getLocks
389
+	// {{{ getLocks
390 390
 
391
-    /**
392
-     * get lock entries for a resource
393
-     *
394
-     * overload this method to return shared and exclusive locks
395
-     * active for this resource
396
-     *
397
-     * @abstract
398
-     * @param string resource path to check
399
-     * @returns array of lock entries each consisting
400
-     *                of 'type' ('shared'/'exclusive'), 'token' and 'timeout'
401
-     */
391
+	/**
392
+	 * get lock entries for a resource
393
+	 *
394
+	 * overload this method to return shared and exclusive locks
395
+	 * active for this resource
396
+	 *
397
+	 * @abstract
398
+	 * @param string resource path to check
399
+	 * @returns array of lock entries each consisting
400
+	 *                of 'type' ('shared'/'exclusive'), 'token' and 'timeout'
401
+	 */
402 402
 
403
-    /* abstract
403
+	/* abstract
404 404
        function getLocks($path)
405 405
        {
406 406
            // dummy entry for PHPDoc
407 407
        }
408 408
     */
409 409
 
410
-    // }}}
411
-
412
-    // }}}
413
-
414
-    // {{{ WebDAV HTTP method wrappers
415
-
416
-    // {{{ options
417
-
418
-    /**
419
-     * OPTIONS method handler
420
-     *
421
-     * The OPTIONS method handler creates a valid OPTIONS reply
422
-     * including Dav: and Allowed: heaers
423
-     * based on the implemented methods found in the actual instance
424
-     *
425
-     * @param void
426
-     * @return void
427
-     */
428
-    function options()
429
-    {
430
-        // get allowed methods
431
-        $allow = $this->_allow();
432
-
433
-        // dav header
434
-        $dav = array(1); // assume we are always dav class 1 compliant
435
-        if (in_array('LOCK', $allow) && in_array('UNLOCK', $allow)) {
436
-            $dav[] = 2; // dav class 2 requires that locking is supported
437
-        }
438
-
439
-        // tell clients what we found
440
-        header('Allow: ' . implode(', ', $allow));
441
-        header('DAV: ' . implode(',', $dav));
442
-        header('Content-Length: 0');
443
-
444
-        // Microsoft clients default to the Frontpage protocol
445
-        // unless we tell them to use WebDAV
446
-        header('MS-Author-Via: DAV');
447
-
448
-        $this->http_status('200 OK');
449
-    }
450
-
451
-    // }}}
452
-
453
-    // {{{ propfind_request_helper
454
-
455
-    /**
456
-     * PROPFIND request helper - prepares data-structures from PROPFIND requests
457
-     *
458
-     * @param options
459
-     * @return void
460
-     */
461
-    function propfind_request_helper(&$options)
462
-    {
463
-        $options = array();
464
-        $options['path'] = $this->path;
465
-
466
-        // get depth from header (default is 'infinity')
467
-        $options['depth'] = 'infinity';
468
-        if (isset($_SERVER['HTTP_DEPTH'])) {
469
-            $options['depth'] = $_SERVER['HTTP_DEPTH'];
470
-        }
471
-
472
-        // analyze request payload
473
-        $parser = new _parse_propfind('php://input');
474
-        if (!$parser->success) {
475
-            $this->http_status('400 Bad Request');
476
-            return;
477
-        }
478
-
479
-        $options['props'] = $parser->props;
480
-
481
-        return true;
482
-    }
483
-
484
-    // }}}
485
-
486
-    // {{{ propfind_response_helper
487
-
488
-    /**
489
-     * PROPFIND response helper - format PROPFIND response
490
-     *
491
-     * @param options
492
-     * @param files
493
-     * @return void
494
-     */
495
-    function propfind_response_helper($options, $files)
496
-    {
497
-        $responses = array();
498
-
499
-        // now loop over all returned files
500
-        foreach ($files as $file) {
501
-
502
-            // collect namespaces here
503
-            // Microsoft need this special namespace for date and time values
504
-            $ns_hash = array(
505
-                'urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882' => 'ns0');
506
-
507
-            $response = array();
508
-
509
-            $response['href'] = $this->getHref($file['path']);
510
-            if (isset($file['href'])) {
511
-                $response['href'] = $file['href'];
512
-            }
513
-
514
-            $response['propstat'] = array();
515
-
516
-            if (is_array($options['props'])) {
517
-
518
-                // loop over all requested properties
519
-                foreach ($options['props'] as $reqprop) {
520
-                    $status = '200 OK';
521
-                    $prop = $this->getProp($reqprop, $file, $options);
522
-
523
-                    if (isset($prop['status'])) {
524
-                        $status = $prop['status'];
525
-                    }
526
-
527
-                    if (!isset($response['propstat'][$status])) {
528
-                        $response['propstat'][$status] = array();
529
-                    }
530
-
531
-                    $response['propstat'][$status][] = $prop;
532
-
533
-                    // namespace handling
534
-                    if (empty($prop['ns']) || // empty namespace
535
-                            $prop['ns'] == 'DAV:' || // default namespace
536
-                            isset($ns_hash[$prop['ns']])) { // already known
537
-                        continue;
538
-                    }
539
-
540
-                    // register namespace
541
-                    $ns_hash[$prop['ns']] = 'ns' . count($ns_hash);
542
-                }
543
-            } else if (is_array($file['props'])) {
544
-
545
-                // loop over all returned properties
546
-                foreach ($file['props'] as $prop) {
547
-                    $status = '200 OK';
548
-
549
-                    if (isset($prop['status'])) {
550
-                        $status = $prop['status'];
551
-                    }
552
-
553
-                    if (!isset($response['propstat'][$status])) {
554
-                        $response['propstat'][$status] = array();
555
-                    }
556
-
557
-                    if ($options['props'] == 'propname') {
558
-
559
-                        // only names of all existing properties were requested
560
-                        // so remove values
561
-                        unset($prop['value']);
562
-                    }
563
-
564
-                    $response['propstat'][$status][] = $prop;
565
-                        unset($prop['value']);
566
-
567
-                    // namespace handling
568
-                    if (empty($prop['ns']) || // empty namespace
569
-                            $prop['ns'] == 'DAV:' || // default namespace
570
-                            isset($ns_hash[$prop['ns']])) { // already known
571
-                        continue;
572
-                    }
573
-
574
-                    // register namespace
575
-                    $ns_hash[$prop['ns']] = 'ns' . count($ns_hash);
576
-                }
577
-            }
578
-
579
-            $response['ns_hash'] = $ns_hash;
580
-            $responses[] = $response;
581
-        }
582
-
583
-        $this->_multistatus($responses);
584
-    }
585
-
586
-    function _multistatus($responses)
587
-    {
588
-        // now we generate the response header...
589
-        $this->http_status('207 Multi-Status');
590
-        header('Content-Type: text/xml; charset="utf-8"');
591
-
592
-        // ...and payload
593
-        echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
594
-        echo "<D:multistatus xmlns:D=\"DAV:\">\n";
595
-
596
-        foreach ($responses as $response) {
597
-
598
-            // ignore empty or incomplete entries
599
-            if (!is_array($response) || empty($response)) {
600
-                continue;
601
-            }
602
-
603
-            $ns_defs = array();
604
-            foreach ($response['ns_hash'] as $name => $prefix) {
605
-                $ns_defs[] = "xmlns:$prefix=\"$name\"";
606
-            }
607
-            echo ' <D:response ' . implode(' ', $ns_defs) . ">\n";
608
-            echo "  <D:href>$response[href]</D:href>\n";
609
-
610
-            // report all found properties and their values (if any)
611
-            // nothing to do if no properties were returend for a file
612
-            if (isset($response['propstat']) &&
613
-                    is_array($response['propstat'])) {
614
-
615
-                foreach ($response['propstat'] as $status => $props) {
616
-                    echo "  <D:propstat>\n";
617
-                    echo "   <D:prop>\n";
618
-
619
-                    foreach ($props as $prop) {
620
-                        if (!is_array($prop) || !isset($prop['name'])) {
621
-                            continue;
622
-                        }
623
-
624
-                        // empty properties (cannot use empty for check as '0'
625
-                        // is a legal value here)
626
-                        if (!isset($prop['value']) || empty($prop['value']) &&
627
-                                $prop['value'] !== 0) {
628
-                            if ($prop['ns'] == 'DAV:') {
629
-                                echo "    <D:$prop[name]/>\n";
630
-                                continue;
631
-                            }
632
-
633
-                            if (!empty($prop['ns'])) {
634
-                                echo '    <' .
635
-                                    $response['ns_hash'][$prop['ns']] .
636
-                                    ":$prop[name]/>\n";
637
-                                continue;
638
-                            }
639
-
640
-                            echo "    <$prop[name] xmlns=\"\"/>";
641
-                            continue;
642
-                        }
643
-
644
-                        // some WebDAV properties need special treatment
645
-                        if ($prop['ns'] == 'DAV:') {
646
-
647
-                            switch ($prop['name']) {
648
-                            case 'creationdate':
649
-                                echo "    <D:creationdate ns0:dt=\"dateTime.tz\">\n";
650
-                                echo '     ' . gmdate('Y-m-d\TH:i:s\Z', $prop['value']) . "\n";
651
-                                echo "    </D:creationdate>\n";
652
-                                break;
653
-
654
-                            case 'getlastmodified':
655
-                                echo "    <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">\n";
656
-                                echo '     ' . gmdate('D, d M Y H:i:s', $prop['value']) . " GMT\n";
657
-                                echo "    </D:getlastmodified>\n";
658
-                                break;
659
-
660
-                            case 'resourcetype':
661
-                                echo "    <D:resourcetype>\n";
662
-                                echo "     <D:$prop[value]/>\n";
663
-                                echo "    </D:resourcetype>\n";
664
-                                break;
665
-
666
-                            case 'supportedlock':
667
-
668
-                                if (is_array($prop[value])) {
669
-                                    $prop[value] = $this->_lockentries($prop[value]);
670
-                                }
671
-                                echo "    <D:supportedlock>\n";
672
-                                echo "     $prop[value]\n";
673
-                                echo "    </D:supportedlock>\n";
674
-                                break;
675
-
676
-                            case 'lockdiscovery':
677
-
678
-                                if (is_array($prop[value])) {
679
-                                    $prop[value] = $this->_activelocks($prop[value]);
680
-                                }
681
-                                echo "    <D:lockdiscovery>\n";
682
-                                echo "     $prop[value]\n";
683
-                                echo "    </D:lockdiscovery>\n";
684
-                                break;
685
-
686
-                            default:
687
-                                echo "    <D:$prop[name]>\n";
688
-                                echo '     ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n";
689
-                                echo "    </D:$prop[name]>\n";
690
-                            }
691
-
692
-                            continue;
693
-                        }
694
-
695
-                        if (!empty($prop['ns'])) {
696
-                            echo '    <' . $response['ns_hash'][$prop['ns']] . ":$prop[name]>\n";
697
-                            echo '     ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n";
698
-                            echo '    </' . $response['ns_hash'][$prop['ns']] . ":$prop[name]>\n";
699
-
700
-                            continue;
701
-                        }
702
-
703
-                        echo "    <$prop[name] xmlns=\"\">\n";
704
-                        echo '     ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n";
705
-                        echo "    </$prop[name]>\n";
706
-                    }
707
-
708
-                    echo "   </D:prop>\n";
709
-                    echo "   <D:status>HTTP/1.1 $status</D:status>\n";
710
-                    echo "  </D:propstat>\n";
711
-                }
712
-            }
713
-
714
-            if (isset($response['status'])) {
715
-                echo "  <D:status>HTTP/1.1 $status</D:status>\n";
716
-            }
717
-
718
-            if (isset($response['responsedescription'])) {
719
-                echo "  <D:responsedescription>\n";
720
-                echo '   ' . $this->_prop_encode(htmlspecialchars($response['responsedescription'])) . "\n";
721
-                echo "  </D:responsedescription>\n";
722
-            }
723
-
724
-            echo " </D:response>\n";
725
-        }
726
-
727
-        echo "</D:multistatus>\n";
728
-    }
729
-
730
-    // }}}
731
-
732
-    // {{{ propfind_wrapper
733
-
734
-    /**
735
-     * PROPFIND method wrapper
736
-     *
737
-     * @param void
738
-     * @return void
739
-     */
740
-    function propfind_wrapper()
741
-    {
742
-        // prepare data-structure from PROPFIND request
743
-        if (!$this->propfind_request_helper($options)) {
744
-            return;
745
-        }
746
-
747
-        // call user handler
748
-        if (!$this->propfind($options, $files)) {
749
-            return;
750
-        }
751
-
752
-        // format PROPFIND response
753
-        $this->propfind_response_helper($options, $files);
754
-    }
755
-
756
-    // }}}
757
-
758
-    // {{{ proppatch_request_helper
759
-
760
-    /**
761
-     * PROPPATCH request helper - prepares data-structures from PROPPATCH requests
762
-     *
763
-     * @param options
764
-     * @return void
765
-     */
766
-    function proppatch_request_helper(&$options)
767
-    {
768
-        $options = array();
769
-        $options['path'] = $this->path;
770
-
771
-        $propinfo = new _parse_proppatch('php://input');
772
-
773
-        if (!$propinfo->success) {
774
-            $this->http_status('400 Bad Request');
775
-            return;
776
-        }
777
-
778
-        $options['props'] = $propinfo->props;
779
-
780
-        return true;
781
-    }
782
-
783
-    // }}}
784
-
785
-    // {{{ proppatch_response_helper
786
-
787
-    /**
788
-     * PROPPATCH response helper - format PROPPATCH response
789
-     *
790
-     * @param options
791
-     * @param responsedescr
792
-     * @return void
793
-     */
794
-    function proppatch_response_helper($options, $responsedescription=null)
795
-    {
796
-        $response = array();
797
-
798
-        $response['href'] = $this->getHref($options['path']);
799
-        if (isset($options['href'])) {
800
-            $response['href'] = $options['href'];
801
-        }
802
-
803
-        $response['propstat'] = array();
804
-
805
-        // collect namespaces here
806
-        $ns_hash = array();
807
-
808
-        if (isset($options['props']) && is_array($options['props'])) {
809
-            foreach ($options['props'] as $prop) {
810
-                $status = '200 OK';
811
-                if (isset($prop['status'])) {
812
-                    $status = $prop['status'];
813
-                }
814
-
815
-                if (!isset($response['propstat'][$status])) {
816
-                    $response['propstat'][$status] = array();
817
-                }
818
-
819
-                $response['propstat'][$status][] = $prop;
820
-
821
-                // namespace handling
822
-                if (empty($prop['ns']) || // empty namespace
823
-                        $prop['ns'] == 'DAV:' || // default namespace
824
-                        isset($ns_hash[$prop['ns']])) { // already known
825
-                    continue;
826
-                }
827
-
828
-                // register namespace
829
-                $ns_hash[$prop['ns']] = 'ns' . count($ns_hash);
830
-            }
831
-        }
832
-
833
-        $response['ns_hash'] = $ns_hash;
834
-        $response['responsedescription'] = $responsedescription;
835
-
836
-        $this->_multistatus(array($response));
837
-    }
838
-
839
-    // }}}
840
-
841
-    // {{{ proppatch_wrapper
842
-
843
-    /**
844
-     * PROPPATCH method wrapper
845
-     *
846
-     * @param void
847
-     * @return void
848
-     */
849
-    function proppatch_wrapper()
850
-    {
851
-        // check resource is not locked
852
-        if (!$this->check_lock_wrapper($this->path)) {
853
-            $this->http_status('423 Locked');
854
-            return;
855
-        }
856
-
857
-        // perpare data-structure from PROPATCH request
858
-        if (!$this->proppatch_request_helper($options)) {
859
-            return;
860
-        }
861
-
862
-        // call user handler
863
-        $responsedescription = $this->proppatch($options);
864
-
865
-        // format PROPPATCH response
866
-        $this->proppatch_response_helper($options, $responsedescription);
867
-    }
868
-
869
-    // }}}
870
-
871
-    // {{{ mkcol_wrapper
872
-
873
-    /**
874
-     * MKCOL method wrapper
875
-     *
876
-     * @param void
877
-     * @return void
878
-     */
879
-    function mkcol_wrapper()
880
-    {
881
-        $options = array();
882
-        $options['path'] = $this->path;
883
-
884
-        $status = $this->mkcol($options);
885
-
886
-        $this->http_status($status);
887
-    }
888
-
889
-    // }}}
890
-
891
-    // {{{ get_request_helper
892
-
893
-    /**
894
-     * GET request helper - prepares data-structures from GET requests
895
-     *
896
-     * @param options
897
-     * @return void
898
-     */
899
-    function get_request_helper(&$options)
900
-    {
901
-        // TODO check for invalid stream
902
-
903
-        $options = array();
904
-        $options['path'] = $this->path;
905
-
906
-        $this->_get_ranges($options);
907
-
908
-        return true;
909
-    }
910
-
911
-    /**
912
-     * parse HTTP Range: header
913
-     *
914
-     * @param  array options array to store result in
915
-     * @return void
916
-     */
917
-    function _get_ranges(&$options)
918
-    {
919
-        // process Range: header if present
920
-        if (isset($_SERVER['HTTP_RANGE'])) {
921
-
922
-            // we only support standard 'bytes' range specifications for now
923
-            if (ereg('bytes[[:space:]]*=[[:space:]]*(.+)', $_SERVER['HTTP_RANGE'], $matches)) {
924
-                $options['ranges'] = array();
925
-
926
-                // ranges are comma separated
927
-                foreach (explode(',', $matches[1]) as $range) {
928
-                    // ranges are either from-to pairs or just end positions
929
-                    list($start, $end) = explode('-', $range);
930
-                    $options['ranges'][] = ($start === '')
931
-                        ? array('last' => $end)
932
-                        : array('start' => $start, 'end' => $end);
933
-                }
934
-            }
935
-        }
936
-    }
937
-
938
-    // }}}
939
-
940
-    // {{{ get_response_helper
941
-
942
-    /**
943
-     * GET response helper - format GET response
944
-     *
945
-     * @param options
946
-     * @param status
947
-     * @return void
948
-     */
949
-    function get_response_helper($options, $status)
950
-    {
951
-        if (empty($status)) {
952
-            $status = '404 Not Found';
953
-        }
954
-
955
-        // set headers before we start printing
956
-        $this->http_status($status);
957
-
958
-        if ($status !== true) {
959
-            return;
960
-        }
961
-
962
-        if (!isset($options['mimetype'])) {
963
-            $options['mimetype'] = 'application/octet-stream';
964
-        }
965
-        header("Content-Type: $options[mimetype]");
966
-
967
-        if (isset($options['mtime'])) {
968
-            header('Last-Modified:' .
969
-                gmdate('D, d M Y H:i:s', $options['mtime']) . 'GMT');
970
-        }
971
-
972
-        if ($options['stream']) {
973
-            // GET handler returned a stream
974
-
975
-            if (!empty($options['ranges']) &&
976
-                    (fseek($options['stream'], 0, SEEK_SET) === 0)) {
977
-                // partial request and stream is seekable
978
-
979
-                if (count($options['ranges']) === 1) {
980
-                    $range = $options['ranges'][0];
981
-
982
-                    if (isset($range['start'])) {
983
-                        fseek($options['stream'], $range['start'], SEEK_SET);
984
-                        if (feof($options['stream'])) {
985
-                            $this->http_status('416 Requested Range Not Satisfiable');
986
-                            return;
987
-                        }
988
-
989
-                        if (isset($range['end'])) {
990
-                            $size = $range['end'] - $range['start'] + 1;
991
-                            $this->http_status('206 Partial');
992
-                            header("Content-Length: $size");
993
-                            header("Content-Range: $range[start]-$range[end]/" .
994
-                                (isset($options['size']) ? $options['size'] : '*'));
995
-                            while ($size && !feof($options['stream'])) {
996
-                                $buffer = fread($options['stream'], 4096);
997
-                                $size -= strlen($buffer);
998
-                                echo $buffer;
999
-                            }
1000
-                        } else {
1001
-                            $this->http_status('206 Partial');
1002
-                            if (isset($options['size'])) {
1003
-                                header("Content-Length: " .
1004
-                                    ($options['size'] - $range['start']));
1005
-                                header("Content-Range: $start-$end/" .
1006
-                                    (isset($options['size']) ? $options['size'] : '*'));
1007
-                            }
1008
-                            fpassthru($options['stream']);
1009
-                        }
1010
-                    } else {
1011
-                        header("Content-Length: $range[last]");
1012
-                        fseek($options['stream'], -$range['last'], SEEK_END);
1013
-                        fpassthru($options['stream']);
1014
-                    }
1015
-                } else {
1016
-                    $this->_multipart_byterange_header(); // init multipart
1017
-                    foreach ($options['ranges'] as $range) {
1018
-
1019
-                        // TODO what if size unknown? 500?
1020
-                        if (isset($range['start'])) {
1021
-                            $from = $range['start'];
1022
-                            $to = !empty($range['end']) ? $range['end'] : $options['size'] - 1;
1023
-                        } else {
1024
-                            $from = $options['size'] - $range['last'] - 1;
1025
-                            $to = $options['size'] - 1;
1026
-                        }
1027
-                        $total = isset($options['size']) ? $options['size'] : '*';
1028
-                        $size = $to - $from + 1;
1029
-                        $this->_multipart_byterange_header($options['mimetype'],
1030
-                            $from, $to, $total);
1031
-
1032
-                        fseek($options['stream'], $start, SEEK_SET);
1033
-                        while ($size && !feof($options['stream'])) {
1034
-                            $buffer = fread($options['stream'], 4096);
1035
-                            $size -= strlen($buffer);
1036
-                            echo $buffer;
1037
-                        }
1038
-                    }
1039
-                    $this->_multipart_byterange_header(); // end multipart
1040
-                }
1041
-            } else {
1042
-                // normal request or stream isn't seekable, return full content
1043
-                if (isset($options['size'])) {
1044
-                    header("Content-Length: $options[size]");
1045
-                }
1046
-
1047
-                fpassthru($options['stream']);
1048
-            }
1049
-        } else if (isset($options['data']))  {
1050
-            if (is_array($options['data'])) {
1051
-                // reply to partial request
1052
-            } else {
1053
-                header("Content-Length: " . strlen($options['data']));
1054
-                echo $options['data'];
1055
-            }
1056
-        }
1057
-    }
1058
-
1059
-    /**
1060
-     * generate separator headers for multipart response
1061
-     *
1062
-     * first and last call happen without parameters to generate
1063
-     * the initial header and closing sequence, all calls inbetween
1064
-     * require content mimetype, start and end byte position and
1065
-     * optionaly the total byte length of the requested resource
1066
-     *
1067
-     * @param  string  mimetype
1068
-     * @param  int     start byte position
1069
-     * @param  int     end   byte position
1070
-     * @param  int     total resource byte size
1071
-     */
1072
-    function _multipart_byterange_header($mimetype = false, $from = false,
1073
-        $to = false, $total = false)
1074
-    {
1075
-        if ($mimetype === false) {
1076
-            if (!isset($this->multipart_separator)) {
1077
-                // init
1078
-                // a little naive, this sequence *might* be part of the content
1079
-                // but it's really not likely and rather expensive to check
1080
-                $this->multipart_separator = 'SEPARATOR_' . md5(microtime());
1081
-
1082
-                // generate HTTP header
1083
-                header('Content-Type: multipart/byteranges; boundary=' .
1084
-                    $this->multipart_separator);
1085
-
1086
-                return;
1087
-            }
1088
-
1089
-            // end
1090
-            // generate closing multipart sequence
1091
-            echo "\n--{$this->multipart_separator}--";
1092
-
1093
-            return;
1094
-        }
1095
-
1096
-        // generate separator and header for next part
1097
-        echo "\n--{$this->multipart_separator}\n";
1098
-        echo "Content-Type: $mimetype\n";
1099
-        echo "Content-Range: $from-$to/"
1100
-            . ($total === false ? "*" : $total) . "\n\n";
1101
-    }
1102
-
1103
-    // }}}
1104
-
1105
-    // {{{ get_wrapper
1106
-
1107
-    /**
1108
-     * GET method wrapper
1109
-     *
1110
-     * @param void
1111
-     * @return void
1112
-     */
1113
-    function get_wrapper()
1114
-    {
1115
-        // perpare data-structure from GET request
1116
-        if (!$this->get_request_helper($options)) {
1117
-            return;
1118
-        }
1119
-
1120
-        // call user handler
1121
-        $status = $this->get($options);
1122
-
1123
-        // format GET response
1124
-        $this->get_response_helper($options, $status);
1125
-    }
1126
-
1127
-    // }}}
1128
-
1129
-    // {{{ head_response_helper
1130
-
1131
-    /**
1132
-     * HEAD response helper - format HEAD response
1133
-     *
1134
-     * @param options
1135
-     * @param status
1136
-     * @return void
1137
-     */
1138
-    function head_response_helper($options, $status)
1139
-    {
1140
-        if (empty($status)) {
1141
-            $status('404 Not Found');
1142
-        }
1143
-
1144
-        // set headers before we start printing
1145
-        $this->http_status($status);
1146
-
1147
-        if ($status !== true) {
1148
-            return;
1149
-        }
1150
-
1151
-        if (!isset($options['mimetype'])) {
1152
-            $options['mimetype'] = 'application/octet-stream';
1153
-        }
1154
-        header("Content-Type: $options[mimetype]");
1155
-
1156
-        if (isset($options['mtime'])) {
1157
-            header('Last-Modified:' .
1158
-                gmdate('D, d M Y H:i:s', $options['mtime']) . 'GMT');
1159
-        }
1160
-
1161
-        if (isset($options['stream'])) {
1162
-            // GET handler returned a stream
1163
-
1164
-            if (!empty($options['ranges']) &&
1165
-                    (fseek($options['stream'], 0, SEEK_SET) === 0)) {
1166
-                // partial request and stream is seekable
1167
-
1168
-                if (count($options['ranges']) === 1) {
1169
-                    $range = $options['ranges'][0];
1170
-
1171
-                    if (isset($range['start'])) {
1172
-                        fseek($options['stream'], $range['start'], SEEK_SET);
1173
-                        if (feof($options['stream'])) {
1174
-                            $this->http_status('416 Requested Range Not Satisfiable');
1175
-                            return;
1176
-                        }
1177
-
1178
-                        if (isset($range['end'])) {
1179
-                            $size = $range['end'] - $range['start'] + 1;
1180
-                            $this->http_status('206 Partial');
1181
-                            header("Content-Length: $size");
1182
-                            header("Content-Range: $range[start]-$range[end]/" .
1183
-                                (isset($options['size']) ? $options['size'] : '*'));
1184
-                        } else {
1185
-                            $this->http_status('206 Partial');
1186
-                            if (isset($options['size'])) {
1187
-                                header("Content-Length: " .
1188
-                                    ($options['size'] - $range['start']));
1189
-                                header("Content-Range: $start-$end/" .
1190
-                                    (isset($options['size']) ? $options['size'] : '*'));
1191
-                            }
1192
-                        }
1193
-                    } else {
1194
-                        header("Content-Length: $range[last]");
1195
-                        fseek($options['stream'], -$range['last'], SEEK_END);
1196
-                    }
1197
-                } else {
1198
-                    $this->_multipart_byterange_header(); // init multipart
1199
-                    foreach ($options['ranges'] as $range) {
1200
-
1201
-                        // TODO what if size unknown? 500?
1202
-                        if (isset($range['start'])) {
1203
-                            $from = $range['start'];
1204
-                            $to = !empty($range['end']) ? $range['end'] :
1205
-                                $options['size'] - 1;
1206
-                        } else {
1207
-                            $from = $options['size'] - $range['last'] - 1;
1208
-                            $to = $options['size'] - 1;
1209
-                        }
1210
-                        $total = isset($options['size']) ? $options['size'] :
1211
-                            '*';
1212
-                        $size = $to - $from + 1;
1213
-                        $this->_multipart_byterange_header($options['mimetype'],
1214
-                            $from, $to, $total);
1215
-
1216
-                        fseek($options['stream'], $start, SEEK_SET);
1217
-                    }
1218
-                    $this->_multipart_byterange_header(); // end multipart
1219
-                }
1220
-            } else {
1221
-                // normal request or stream isn't seekable, return full content
1222
-                if (isset($options['size'])) {
1223
-                    header("Content-Length: $options[size]");
1224
-                }
1225
-            }
1226
-        } else if (isset($options['data']))  {
1227
-            if (is_array($options['data'])) {
1228
-                // reply to partial request
1229
-            } else {
1230
-                header("Content-Length: " . strlen($options['data']));
1231
-            }
1232
-        }
1233
-    }
1234
-
1235
-    // }}}
1236
-
1237
-    // {{{ head_wrapper
1238
-
1239
-    /**
1240
-     * HEAD method wrapper
1241
-     *
1242
-     * @param void
1243
-     * @return void
1244
-     */
1245
-    function head_wrapper()
1246
-    {
1247
-        $options = array();
1248
-        $options['path'] = $this->path;
1249
-
1250
-        // call user handler
1251
-        if (method_exists($this, 'head')) {
1252
-            $status = $this->head($options);
1253
-        } else {
1254
-
1255
-            // can emulate HEAD using GET
1256
-            ob_start();
1257
-            $status = $this->get($options);
1258
-            ob_end_clean();
1259
-        }
1260
-
1261
-        // format HEAD response
1262
-        $this->head_response_helper($options, $status);
1263
-    }
1264
-
1265
-    // }}}
1266
-
1267
-    // {{{ put_request_helper
1268
-
1269
-    /**
1270
-     * PUT request helper - prepares data-structures from PUT requests
1271
-     *
1272
-     * @param options
1273
-     * @return void
1274
-     */
1275
-    function put_request_helper(&$options)
1276
-    {
1277
-        $options = array();
1278
-        $options['path'] = $this->path;
1279
-        $options['content_length'] = $_SERVER['CONTENT_LENGTH'];
1280
-
1281
-        // get the content-type
1282
-        if (isset($_SERVER['CONTENT_TYPE'])) {
1283
-
1284
-            // for now we do not support any sort of multipart requests
1285
-            if (!strncmp($_SERVER['CONTENT_TYPE'], 'multipart/', 10)) {
1286
-                $this->http_status('501 Not Implemented');
1287
-                echo 'The service does not support mulipart PUT requests';
1288
-                return;
1289
-            }
1290
-
1291
-            $options['content_type'] = $_SERVER['CONTENT_TYPE'];
1292
-        } else {
1293
-
1294
-            // default content type if none given
1295
-            $options['content_type'] = 'application/unknown';
1296
-        }
1297
-
1298
-        // RFC2616 2.6 says: The recipient of the entity MUST NOT
1299
-        // ignore any Content-* (e.g. Content-Range) headers that it
1300
-        // does not understand or implement and MUST return a 501
1301
-        // (Not Implemented) response in such cases.
1302
-        foreach ($_SERVER as $key => $value) {
1303
-            if (strncmp($key, 'HTTP_CONTENT', 11)) continue;
1304
-            switch ($key) {
1305
-            case 'HTTP_CONTENT_ENCODING': // RFC2616 14.11
1306
-
1307
-                // TODO support this if ext/zlib filters are available
1308
-                $this->http_status('501 Not Implemented');
1309
-                echo "The service does not support '$value' content encoding";
1310
-                return;
1311
-
1312
-            case 'HTTP_CONTENT_LANGUAGE': // RFC2616 14.12
1313
-
1314
-                // we assume it is not critical if this one is ignored
1315
-                // in the actual PUT implementation...
1316
-                $options['content_language'] = $value;
1317
-                break;
1318
-
1319
-            case 'HTTP_CONTENT_LOCATION': // RFC2616 14.14
1320
-
1321
-                // The meaning of the Content-Location header in PUT
1322
-                // or POST requests is undefined; servers are free
1323
-                // to ignore it in those cases. */
1324
-                break;
1325
-
1326
-            case 'HTTP_CONTENT_RANGE': // RFC2616 14.16
1327
-
1328
-                // single byte range requests are supported
1329
-                // the header format is also specified in RFC2616 14.16
1330
-                // TODO we have to ensure that implementations support this or send 501 instead
1331
-                if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1332
-                    $this->http_status('400 Bad Request');
1333
-                    echo 'The service does only support single byte ranges';
1334
-                    return;
1335
-                }
1336
-
1337
-                $range = array('start' => $matches[1], 'end' => $matches[2]);
1338
-                if (is_numeric($matches[3])) {
1339
-                    $range['total_length'] = $matches[3];
1340
-                }
1341
-                $option['ranges'][] = $range;
1342
-
1343
-                // TODO make sure the implementation supports partial PUT
1344
-                // this has to be done in advance to avoid data being overwritten
1345
-                // on implementations that do not support this...
1346
-                break;
1347
-
1348
-            case 'HTTP_CONTENT_MD5': // RFC2616 14.15
1349
-
1350
-                // TODO maybe we can just pretend here?
1351
-                $this->http_status('501 Not Implemented');
1352
-                echo 'The service does not support content MD5 checksum verification';
1353
-                return;
1354
-
1355
-            default:
1356
-
1357
-                // any other unknown Content-* headers
1358
-                $this->http_status('501 Not Implemented');
1359
-                echo "The service does not support '$key'";
1360
-                return;
1361
-            }
1362
-        }
1363
-
1364
-        $options['stream'] = fopen('php://input', 'r');
1365
-
1366
-        return true;
1367
-    }
1368
-
1369
-    // }}}
1370
-
1371
-    // {{{ put_response_helper
1372
-
1373
-    /**
1374
-     * PUT response helper - format PUT response
1375
-     *
1376
-     * @param options
1377
-     * @param status
1378
-     * @return void
1379
-     */
1380
-    function put_response_helper($options, $status)
1381
-    {
1382
-        if (empty($status)) {
1383
-            $status = '403 Forbidden';
1384
-        } else if (is_resource($status) &&
1385
-                get_resource_type($status) == 'stream') {
1386
-            $stream = $status;
1387
-            $status = $options['new'] === false ? '204 No Content' :
1388
-                '201 Created';
1389
-
1390
-            if (!empty($options['ranges'])) {
1391
-
1392
-                // TODO multipart support is missing (see also above)
1393
-                if (0 == fseek($stream, $range[0]['start'], SEEK_SET)) {
1394
-                    $length = $range[0]['end'] - $range[0]['start'] + 1;
1395
-                    if (!fwrite($stream, fread($options['stream'], $length))) {
1396
-                        $status = '403 Forbidden';
1397
-                    }
1398
-                } else {
1399
-                    $status = '403 Forbidden';
1400
-                }
1401
-            } else {
1402
-                while (!feof($options['stream'])) {
1403
-                    $buf = fread($options['stream'], 4096);
1404
-                    if (fwrite($stream, $buf) != 4096) {
1405
-                        break;
1406
-                    }
1407
-                }
1408
-            }
1409
-
1410
-            fclose($stream);
1411
-        }
1412
-
1413
-        $this->http_status($status);
1414
-    }
1415
-
1416
-    // }}}
1417
-
1418
-    // {{{ put_wrapper
1419
-
1420
-    /**
1421
-     * PUT method wrapper
1422
-     *
1423
-     * @param void
1424
-     * @return void
1425
-     */
1426
-    function put_wrapper()
1427
-    {
1428
-        // check resource is not locked
1429
-        if (!$this->check_lock_wrapper($this->path)) {
1430
-            $this->http_status('423 Locked');
1431
-            return;
1432
-        }
1433
-
1434
-        // perpare data-structure from PUT request
1435
-        if (!$this->put_request_helper($options)) {
1436
-            return;
1437
-        }
1438
-
1439
-        // call user handler
1440
-        $status = $this->put($options);
1441
-
1442
-        // format PUT response
1443
-        $this->put_response_helper($options, $status);
1444
-    }
1445
-
1446
-    // }}}
1447
-
1448
-    // {{{ delete_wrapper
1449
-
1450
-    /**
1451
-     * DELETE method wrapper
1452
-     *
1453
-     * @param void
1454
-     * @return void
1455
-     */
1456
-    function delete_wrapper()
1457
-    {
1458
-        // RFC2518 9.2 last paragraph
1459
-        if (isset($_SERVER['HTTP_DEPTH']) &&
1460
-                $_SERVER['HTTP_DEPTH'] != 'infinity') {
1461
-            $this->http_status('400 Bad Request');
1462
-            return;
1463
-        }
1464
-
1465
-        // check resource is not locked
1466
-        if (!$this->check_lock_wrapper($this->path)) {
1467
-            $this->http_status('423 Locked');
1468
-            return;
1469
-        }
1470
-
1471
-        $options = array();
1472
-        $options['path'] = $this->path;
1473
-
1474
-        // call user handler
1475
-        $status = $this->delete($options);
1476
-        if ($status === true) {
1477
-            $status = '204 No Content';
1478
-        }
1479
-
1480
-        $this->http_status($status);
1481
-    }
1482
-
1483
-    // }}}
1484
-
1485
-    // {{{ copymove_request_helper
1486
-
1487
-    /**
1488
-     * COPY/MOVE request helper - prepares data-structures from COPY/MOVE
1489
-     * requests
1490
-     *
1491
-     * @param options
1492
-     * @return void
1493
-     */
1494
-    function copymove_request_helper(&$options)
1495
-    {
1496
-        $options = array();
1497
-        $options['path'] = $this->path;
1498
-
1499
-        $options['depth'] = 'infinity';
1500
-        if (isset($_SERVER['HTTP_DEPTH'])) {
1501
-            $options['depth'] = $_SERVER['HTTP_DEPTH'];
1502
-        }
1503
-
1504
-        // RFC2518 9.6, 8.8.4 and 8.9.3
1505
-        $options['overwrite'] = true;
1506
-        if (isset($_SERVER['HTTP_OVERWRITE'])) {
1507
-            $options['overwrite'] = $_SERVER['HTTP_OVERWRITE'] == 'T';
1508
-        }
1509
-
1510
-        list ($src_host, $src_port) = explode(':', $_SERVER['HTTP_HOST']);
1511
-        if (empty($src_port)) {
1512
-            $src_port = 80;
1513
-        }
1514
-
1515
-        $dst_url = parse_url($_SERVER['HTTP_DESTINATION']);
1516
-        $dst_host = $dst_url['host'];
1517
-
1518
-        $dst_port = $dst_url['port'];
1519
-        if (empty($dst_port)) {
1520
-            $dst_port = 80;
1521
-        }
1522
-
1523
-        $dst_path = $dst_url['path'];
1524
-
1525
-        // base_uri is urldecoded
1526
-        $dst_path = $this->_urldecode($dst_path);
1527
-
1528
-        // does the destination resource belong on this server?
1529
-        if ($dst_host == $src_host && $dst_port == $src_port &&
1530
-                !strncmp($dst_path, $this->base_uri, strlen($this->base_uri))) {
1531
-            $options['dest'] = substr($dst_path, strlen($this->base_uri));
1532
-
1533
-            $options['dest'] = ltrim($options['dest'], '/');
1534
-
1535
-            // check source & destination are not the same - data could be lost
1536
-            // if overwrite is true - RFC2518 8.8.5
1537
-            if ($options['dest'] == $this->path) {
1538
-                $this->http_status('403 Forbidden');
1539
-                return;
1540
-            }
1541
-
1542
-            return true;
1543
-        }
1544
-
1545
-        $options['dest_url'] = $_SERVER['HTTP_DESTINATION'];
1546
-
1547
-        return true;
1548
-    }
1549
-
1550
-    // }}}
1551
-
1552
-    // {{{ copy_wrapper
1553
-
1554
-    /**
1555
-     * COPY method wrapper
1556
-     *
1557
-     * @param void
1558
-     * @return void
1559
-     */
1560
-    function copy_wrapper()
1561
-    {
1562
-        // no need to check source is not locked
1563
-
1564
-        // perpare data-structure from COPY request
1565
-        if (!$this->copymove_request_helper($options)) {
1566
-            return;
1567
-        }
1568
-
1569
-        // check destination is not locked
1570
-        if (isset($options['dest']) &&
1571
-                !$this->check_lock_wrapper($options['dest'])) {
1572
-            $this->http_status('423 Locked');
1573
-            return;
1574
-        }
1575
-
1576
-        // call user handler
1577
-        $status = $this->copy($options);
1578
-        if ($status === true) {
1579
-            $status = $options['new'] === false ? '204 No Content' :
1580
-                '201 Created';
1581
-        }
1582
-
1583
-        $this->http_status($status);
1584
-    }
1585
-
1586
-    // }}}
1587
-
1588
-    // {{{ move_wrapper
1589
-
1590
-    /**
1591
-     * MOVE method wrapper
1592
-     *
1593
-     * @param void
1594
-     * @return void
1595
-     */
1596
-    function move_wrapper()
1597
-    {
1598
-        // check resource is not locked
1599
-        if (!$this->check_lock_wrapper($this->path)) {
1600
-            $this->http_status('423 Locked');
1601
-            return;
1602
-        }
1603
-
1604
-        // perpare data-structure from MOVE request
1605
-        if (!$this->copymove_request_helper($options)) {
1606
-            return;
1607
-        }
1608
-
1609
-        // check destination is not locked
1610
-        if (isset($options['dest']) &&
1611
-                !$this->check_lock_wrapper($options['dest'])) {
1612
-            $this->http_status('423 Locked');
1613
-            return;
1614
-        }
1615
-
1616
-        // call user handler
1617
-        $status = $this->move($options);
1618
-        if ($status === true) {
1619
-            $status = $options['new'] === false ? '204 No Content' :
1620
-                '201 Created';
1621
-        }
1622
-
1623
-        $this->http_status($status);
1624
-    }
1625
-
1626
-    // }}}
1627
-
1628
-    // {{{ lock_request_helper
1629
-
1630
-    /**
1631
-     * LOCK request helper - prepares data-structures from LOCK requests
1632
-     *
1633
-     * @param options
1634
-     * @return void
1635
-     */
1636
-    function lock_request_helper(&$options)
1637
-    {
1638
-        $options = array();
1639
-        $options['path'] = $this->path;
1640
-
1641
-        $options['depth'] = 'infinity';
1642
-        if (isset($_SERVER['HTTP_DEPTH'])) {
1643
-            $options['depth'] = $_SERVER['HTTP_DEPTH'];
1644
-        }
1645
-
1646
-        if (isset($_SERVER['HTTP_TIMEOUT'])) {
1647
-            $options['timeout'] = explode(',', $_SERVER['HTTP_TIMEOUT']);
1648
-        }
1649
-
1650
-        if (empty($_SERVER['CONTENT_LENGTH']) && !empty($_SERVER['HTTP_IF'])) {
1651
-
1652
-            // refresh lock
1653
-            $options['update'] = substr($_SERVER['HTTP_IF'], 2, -2);
1654
-
1655
-            return true;
1656
-        }
1657
-
1658
-        // extract lock request information from request XML payload
1659
-        $lockinfo = new _parse_lockinfo('php://input');
1660
-        if (!$lockinfo->success) {
1661
-            $this->http_status('400 Bad Request');
1662
-            return;
1663
-        }
1664
-
1665
-        // new lock
1666
-        $options['scope'] = $lockinfo->lockscope;
1667
-        $options['type']  = $lockinfo->locktype;
1668
-        $options['owner'] = $lockinfo->owner;
1669
-
1670
-        $options['token'] = $this->_new_locktoken();
1671
-
1672
-        return true;
1673
-    }
1674
-
1675
-    // }}}
1676
-
1677
-    // {{{ lock_response_helper
1678
-
1679
-    /**
1680
-     * LOCK response helper - format LOCK response
1681
-     *
1682
-     * @param options
1683
-     * @param status
1684
-     * @return void
1685
-     */
1686
-    function lock_response_helper($options, $status)
1687
-    {
1688
-        if (isset($options['locks']) && is_array($options['locks'])) {
1689
-            $this->http_status('409 Conflict');
1690
-
1691
-            $responses = array();
1692
-            foreach ($options['locks'] as $lock) {
1693
-                $response = array();
1694
-
1695
-                $response['href'] = $this->getHref($lock['path']);
1696
-                if (isset($lock['href'])) {
1697
-                    $response['href'] = $lock['href'];
1698
-                }
1699
-
1700
-                $response['status'] = '423 Locked';
1701
-
1702
-                $responses[] = $response;
1703
-            }
1704
-
1705
-            $this->_multistatus($responses);
1706
-            return;
1707
-        }
1708
-
1709
-        if (is_bool($status)) {
1710
-            $status = $status ? '200 OK' : '423 Locked';
1711
-        }
1712
-
1713
-        // set headers before we start printing
1714
-        $this->http_status($status);
1715
-
1716
-        if ($status{0} == 2) { // 2xx states are ok
1717
-            header('Content-Type: text/xml; charset="utf-8"');
1718
-            header("Lock-Token: <$options[token]>");
1719
-
1720
-            echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1721
-            echo "<D:prop xmlns:D=\"DAV:\">\n";
1722
-            echo " <D:lockdiscovery>\n";
1723
-            echo '  ' . $this->_activelocks(array($options)) . "\n";
1724
-            echo " </D:lockdiscovery>\n";
1725
-            echo "</D:prop>\n";
1726
-        }
1727
-    }
1728
-
1729
-    // }}}
1730
-
1731
-    // {{{ lock_wrapper
1732
-
1733
-    /**
1734
-     * LOCK method wrapper
1735
-     *
1736
-     * @param void
1737
-     * @return void
1738
-     */
1739
-    function lock_wrapper()
1740
-    {
1741
-        // perpare data-structure from LOCK request
1742
-        if (!$this->lock_request_helper($options)) {
1743
-            return;
1744
-        }
1745
-
1746
-        // check resource is not locked
1747
-        if (isset($options['update']) &&
1748
-                !$this->check_lock_wrapper($this->path) ||
1749
-                !$this->check_lock_wrapper($this->path,
1750
-                $options['scope'] == 'shared')) {
1751
-            $this->http_status('423 Locked');
1752
-            return;
1753
-        }
1754
-
1755
-        $options['locks'] = $this->getDescendentsLocks($this->path);
1756
-        if (empty($options['locks'])) {
1757
-
1758
-            // call user handler
1759
-            $status = $this->lock($options);
1760
-        }
1761
-
1762
-        // format LOCK response
1763
-        $this->lock_response_helper($options, $status);
1764
-    }
1765
-
1766
-    // }}}
1767
-
1768
-    // {{{ unlock_request_helper
1769
-
1770
-    /**
1771
-     * UNLOCK request helper - prepares data-structures from UNLOCK requests
1772
-     *
1773
-     * @param options
1774
-     * @return void
1775
-     */
1776
-    function unlock_request_helper(&$options)
1777
-    {
1778
-        $options = array();
1779
-        $options['path'] = $this->path;
1780
-
1781
-        // strip surrounding <>
1782
-        $options['token'] = substr(trim($_SERVER['HTTP_LOCK_TOKEN']), 1, -1);
1783
-
1784
-        return true;
1785
-    }
1786
-
1787
-    // }}}
1788
-
1789
-    // {{{ unlock_wrapper
1790
-
1791
-    /**
1792
-     * UNLOCK method wrapper
1793
-     *
1794
-     * @param void
1795
-     * @return void
1796
-     */
1797
-    function unlock_wrapper()
1798
-    {
1799
-        // perpare data-structure from DELETE request
1800
-        if (!$this->unlock_request_helper($options)) {
1801
-            return;
1802
-        }
1803
-
1804
-        // call user handler
1805
-        $status = $this->unlock($options);
1806
-
1807
-        // RFC2518 8.11.1
1808
-        if ($status === true) {
1809
-            $status = '204 No Content';
1810
-        }
1811
-
1812
-        $this->http_status($status);
1813
-    }
1814
-
1815
-    // }}}
1816
-
1817
-    function getHref($path)
1818
-    {
1819
-        return $this->base_uri . '/' . $path;
1820
-    }
1821
-
1822
-    function getProp($reqprop, $file, $options)
1823
-    {
1824
-        // check if property exists in response
1825
-        foreach ($file['props'] as $prop) {
1826
-            if ($reqprop['name'] == $prop['name'] &&
1827
-                    $reqprop['ns'] == $prop['ns']) {
1828
-                return $prop;
1829
-            }
1830
-        }
1831
-
1832
-        if ($reqprop['name'] == 'lockdiscovery' &&
1833
-                $reqprop['ns'] == 'DAV:' &&
1834
-                method_exists($this, 'getLocks')) {
1835
-
1836
-            return $this->mkprop('DAV:', 'lockdiscovery',
1837
-                $this->getLocks($file['path']));
1838
-        }
1839
-
1840
-        // incase the requested property had a value, like calendar-data
1841
-        unset($reqprop['value']);
1842
-        $reqprop['status'] = '404 Not Found';
1843
-        return $reqprop;
1844
-    }
1845
-
1846
-    function getDescendentsLocks($path)
1847
-    {
1848
-        $options = array();
1849
-        $options['path'] = $path;
1850
-        $options['depth'] = 'infinity';
1851
-        $options['props'] = array();
1852
-        $options['props'][] = $this->mkprop('DAV:', 'lockdiscovery', null);
1853
-
1854
-        // call user handler
1855
-        if (!$this->propfind($options, $files)) {
1856
-            return;
1857
-        }
1858
-
1859
-        return $files;
1860
-    }
1861
-
1862
-    // {{{ _allow()
1863
-
1864
-    /**
1865
-     * list implemented methods
1866
-     *
1867
-     * @param void
1868
-     * @return array something
1869
-     */
1870
-    function _allow()
1871
-    {
1872
-        // OPTIONS is always there
1873
-        $allow = array('OPTIONS');
1874
-
1875
-        // all other methods need both a method_wrapper() and a method()
1876
-        // implementation
1877
-        // the base class defines only wrappers
1878
-        foreach(get_class_methods($this) as $method) {
1879
-
1880
-            // strncmp breaks with negative len -
1881
-            // http://bugs.php.net/bug.php?id=36944
1882
-            //if (!strncmp('_wrapper', $method, -8)) {
1883
-            if (!strcmp(substr($method, -8), '_wrapper')) {
1884
-                $method = strtolower(substr($method, 0, -8));
1885
-                if (method_exists($this, $method) &&
1886
-                        ($method != 'lock' && $method != 'unlock' ||
1887
-                        method_exists($this, 'getLocks'))) {
1888
-                    $allow[] = $method;
1889
-                }
1890
-            }
1891
-        }
1892
-
1893
-        // we can emulate a missing HEAD implemetation using GET
1894
-        if (in_array('GET', $allow)) {
1895
-            $allow[] = 'HEAD';
1896
-        }
1897
-
1898
-        return $allow;
1899
-    }
1900
-
1901
-    // }}}
1902
-
1903
-    // {{{ mkprop
1904
-
1905
-    /**
1906
-     * helper for property element creation
1907
-     *
1908
-     * @param  string XML namespace (optional)
1909
-     * @param  string property name
1910
-     * @param  string property value
1911
-     * @return array  property array
1912
-     */
1913
-    function mkprop()
1914
-    {
1915
-        $args = func_get_args();
1916
-
1917
-        $prop = array();
1918
-        $prop['ns'] = 'DAV:';
1919
-        if (count($args) > 2) {
1920
-            $prop['ns'] = array_shift($args);
1921
-        }
1922
-
1923
-        $prop['name'] = array_shift($args);
1924
-        $prop['value'] = array_shift($args);
1925
-        $prop['status'] = array_shift($args);
1926
-
1927
-        return $prop;
1928
-    }
1929
-
1930
-    // }}}
1931
-
1932
-    // {{{ check_auth_wrapper
1933
-
1934
-    /**
1935
-     * check authentication if implemented
1936
-     *
1937
-     * @param  void
1938
-     * @return bool  true if authentication succeded or not necessary
1939
-     */
1940
-    function check_auth_wrapper()
1941
-    {
1942
-        if (method_exists($this, 'checkAuth')) {
1943
-
1944
-            // PEAR style method name
1945
-            return $this->checkAuth(@$_SERVER['AUTH_TYPE'],
1946
-                @$_SERVER['PHP_AUTH_USER'],
1947
-                @$_SERVER['PHP_AUTH_PW']);
1948
-        }
1949
-
1950
-        if (method_exists($this, 'check_auth')) {
1951
-
1952
-            // old (pre 1.0) method name
1953
-            return $this->check_auth(@$_SERVER['AUTH_TYPE'],
1954
-                @$_SERVER['PHP_AUTH_USER'],
1955
-                @$_SERVER['PHP_AUTH_PW']);
1956
-        }
1957
-
1958
-        // no method found -> no authentication required
1959
-        return true;
1960
-    }
1961
-
1962
-    // }}}
1963
-
1964
-    // {{{ UUID stuff
1965
-
1966
-    /**
1967
-     * generate Unique Universal IDentifier for lock token
1968
-     *
1969
-     * @param void
1970
-     * @return string a new UUID
1971
-     */
1972
-    function _new_uuid()
1973
-    {
1974
-        // use uuid extension from PECL if available
1975
-        if (function_exists('uuid_create')) {
1976
-            return uuid_create();
1977
-        }
1978
-
1979
-        // fallback
1980
-        $uuid = md5(microtime() . getmypid()); // this should be random enough for now
1981
-
1982
-        // set variant and version fields for 'true' random uuid
1983
-        $uuid{12} = '4';
1984
-        $n = 8 + (ord($uuid{16}) & 3);
1985
-        $hex = '0123456789abcdef';
1986
-        $uuid{16} = $hex{$n};
1987
-
1988
-        // return formated uuid
1989
-        return substr($uuid,  0, 8) . '-' .
1990
-            substr($uuid,  8, 4) . '-' .
1991
-            substr($uuid, 12, 4) . '-' .
1992
-            substr($uuid, 16, 4) . '-' .
1993
-            substr($uuid, 20);
1994
-    }
1995
-
1996
-    /**
1997
-     * create a new opaque lock token as defined in RFC2518
1998
-     *
1999
-     * @param  void
2000
-     * @return string new RFC2518 opaque lock token
2001
-     */
2002
-    function _new_locktoken()
2003
-    {
2004
-        return 'opaquelocktoken:' . $this->_new_uuid();
2005
-    }
2006
-
2007
-    // }}}
2008
-
2009
-    // {{{ WebDAV If: header parsing
2010
-
2011
-    /**
2012
-     *
2013
-     *
2014
-     * @param string header string to parse
2015
-     * @param int current parsing position
2016
-     * @return array next token (type and value)
2017
-     */
2018
-    function _if_header_lexer($string, &$pos)
2019
-    {
2020
-        // skip whitespace
2021
-        while (ctype_space($string{$pos})) {
2022
-            ++$pos;
2023
-        }
2024
-
2025
-        // already at end of string?
2026
-        if (strlen($string) <= $pos) {
2027
-            return;
2028
-        }
2029
-
2030
-        // get next character
2031
-        $c = $string{$pos++};
2032
-
2033
-        // now it depends on what we found
2034
-        switch ($c) {
2035
-
2036
-            // URIs are enclosed in <...>
2037
-            case '<':
2038
-                $pos2 = strpos($string, '>', $pos);
2039
-                $uri = substr($string, $pos, $pos2 - $pos);
2040
-                $pos = $pos2 + 1;
2041
-                return array('URI', $uri);
2042
-
2043
-            // ETags are enclosed in [...]
2044
-            case '[':
2045
-                $type = 'ETAG_STRONG';
2046
-                if ($string{$pos} == 'W') {
2047
-                    $type = 'ETAG_WEAK';
2048
-                    $pos += 2;
2049
-                }
2050
-
2051
-                $pos2 = strpos($string, ']', $pos);
2052
-                $etag = substr($string, $pos + 1, $pos2 - $pos - 2);
2053
-                $pos = $pos2 + 1;
2054
-                return array($type, $etag);
2055
-
2056
-            // 'N' indicates negation
2057
-            case 'N':
2058
-                $pos += 2;
2059
-                return array('NOT', 'Not');
2060
-
2061
-            // anything else is passed verbatim char by char
2062
-            default:
2063
-                return array('CHAR', $c);
2064
-        }
2065
-    }
2066
-
2067
-    /**
2068
-     * parse If: header
2069
-     *
2070
-     * @param  string  header string
2071
-     * @return array   URIs and their conditions
2072
-     */
2073
-    function _if_header_parser($str)
2074
-    {
2075
-        $pos = 0;
2076
-        $len = strlen($str);
2077
-
2078
-        $uris = array();
2079
-
2080
-        // parser loop
2081
-        while ($pos < $len) {
2082
-
2083
-            // get next token
2084
-            $token = $this->_if_header_lexer($str, $pos);
2085
-
2086
-            // check for URI
2087
-            $uri = '';
2088
-            if ($token[0] == 'URI') {
2089
-                $uri = $token[1]; // remember URI
2090
-                $token = $this->_if_header_lexer($str, $pos); // get next token
2091
-            }
2092
-
2093
-            // sanity check
2094
-            if ($token[0] != 'CHAR' || $token[1] != '(') {
2095
-                return;
2096
-            }
2097
-
2098
-            $list = array();
2099
-            $level = 1;
2100
-            while ($level) {
2101
-                $token = $this->_if_header_lexer($str, $pos);
2102
-
2103
-                $not = '';
2104
-                if ($token[0] == 'NOT') {
2105
-                    $not = '!';
2106
-                    $token = $this->_if_header_lexer($str, $pos);
2107
-                }
2108
-
2109
-                switch ($token[0]) {
2110
-                    case 'CHAR':
2111
-                        switch ($token[1]) {
2112
-                            case '(':
2113
-                                $level++;
2114
-                                break;
2115
-
2116
-                            case ')':
2117
-                                $level--;
2118
-                                break;
2119
-
2120
-                            default:
2121
-                                return;
2122
-                        }
2123
-                        break;
2124
-
2125
-                    case 'URI':
2126
-                        $list[] = $not . "<$token[1]>";
2127
-                        break;
2128
-
2129
-                    case 'ETAG_WEAK':
2130
-                        $list[] = $not . "[W/'$token[1]']>";
2131
-                        break;
2132
-
2133
-                    case 'ETAG_STRONG':
2134
-                        $list[] = $not . "['$token[1]']>";
2135
-                        break;
2136
-
2137
-                    default:
2138
-                        return;
2139
-                }
2140
-            }
2141
-
2142
-            if (is_array($uris[$uri])) {
2143
-                $uris[$uri] = array_merge($uris[$uri], $list);
2144
-                continue;
2145
-            }
2146
-            $uris[$uri] = $list;
2147
-        }
2148
-
2149
-        return $uris;
2150
-    }
2151
-
2152
-    /**
2153
-     * check if conditions from If: headers are met
2154
-     *
2155
-     * the If: header is an extension to HTTP/1.1
2156
-     * defined in RFC2518 9.4
2157
-     *
2158
-     * @param  void
2159
-     * @return void
2160
-     */
2161
-    function _check_if_header_conditions()
2162
-    {
2163
-        if (!isset($_SERVER['HTTP_IF'])) {
2164
-            return true;
2165
-        }
2166
-
2167
-        $this->_if_header_uris =
2168
-            $this->_if_header_parser($_SERVER['HTTP_IF']);
2169
-
2170
-        // any match is ok
2171
-        foreach($this->_if_header_uris as $uri => $conditions) {
2172
-            if (empty($uri)) {
2173
-                $uri = $this->base_uri . '/' . $this->path;
2174
-            }
2175
-
2176
-            // all must match
2177
-            foreach ($conditions as $condition) {
2178
-
2179
-                // lock tokens may be free form (RFC2518 6.3)
2180
-                // but if opaquelocktokens are used (RFC2518 6.4)
2181
-                // we have to check the format (litmus tests this)
2182
-                if (!strncmp($condition, '<opaquelocktoken:', strlen('<opaquelocktoken'))) {
2183
-                    if (!ereg('^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$', $condition)) {
2184
-                        return;
2185
-                    }
2186
-                }
2187
-
2188
-                if (!$this->_check_uri_condition($uri, $condition)) {
2189
-                    continue (2);
2190
-                }
2191
-            }
2192
-
2193
-            return true;
2194
-        }
2195
-    }
2196
-
2197
-    /**
2198
-     * Check a single URI condition parsed from an if-header
2199
-     *
2200
-     * @abstract
2201
-     * @param string $uri URI to check
2202
-     * @param string $condition Condition to check for this URI
2203
-     * @returns bool Condition check result
2204
-     */
2205
-    function _check_uri_condition($uri, $condition)
2206
-    {
2207
-        // not really implemented here,
2208
-        // implementations must override
2209
-        return true;
2210
-    }
2211
-
2212
-    /**
2213
-     * @param array of locks
2214
-     * @param bool exclusive lock?
2215
-     */
2216
-    function check_lock_helper($lock, $exclusive_only = false)
2217
-    {
2218
-        if (!is_array($lock) || empty($lock)) {
2219
-            return true;
2220
-        }
2221
-
2222
-        // FIXME doesn't check uri restrictions yet
2223
-        if (strstr($_SERVER['HTTP_IF'], $lock['token'])) {
2224
-            return true;
2225
-        }
2226
-
2227
-        if ($exclusive_only && ($lock['scope'] == 'shared')) {
2228
-            return true;
2229
-        }
2230
-    }
2231
-
2232
-    /**
2233
-     * @param string path of resource to check
2234
-     * @param bool exclusive lock?
2235
-     */
2236
-    function check_lock_wrapper($path, $exclusive_only = false)
2237
-    {
2238
-        if (!method_exists($this, 'getLocks')) {
2239
-            return true;
2240
-        }
2241
-
2242
-        $lock = $this->getLocks($path);
2243
-
2244
-        return $this->check_lock_helper($lock, $exclusive_only);
2245
-    }
2246
-
2247
-    // }}}
2248
-
2249
-    function _lockentries($locks)
2250
-    {
2251
-        if (!is_array($locks) || empty($locks)) {
2252
-            return '';
2253
-        }
2254
-
2255
-        foreach ($locks as $key => $lock) {
2256
-            if (!is_array($lock) || empty($lock)) {
2257
-                continue;
2258
-            }
2259
-
2260
-            $locks[$key] = "<D:lockentry>
410
+	// }}}
411
+
412
+	// }}}
413
+
414
+	// {{{ WebDAV HTTP method wrappers
415
+
416
+	// {{{ options
417
+
418
+	/**
419
+	 * OPTIONS method handler
420
+	 *
421
+	 * The OPTIONS method handler creates a valid OPTIONS reply
422
+	 * including Dav: and Allowed: heaers
423
+	 * based on the implemented methods found in the actual instance
424
+	 *
425
+	 * @param void
426
+	 * @return void
427
+	 */
428
+	function options()
429
+	{
430
+		// get allowed methods
431
+		$allow = $this->_allow();
432
+
433
+		// dav header
434
+		$dav = array(1); // assume we are always dav class 1 compliant
435
+		if (in_array('LOCK', $allow) && in_array('UNLOCK', $allow)) {
436
+			$dav[] = 2; // dav class 2 requires that locking is supported
437
+		}
438
+
439
+		// tell clients what we found
440
+		header('Allow: ' . implode(', ', $allow));
441
+		header('DAV: ' . implode(',', $dav));
442
+		header('Content-Length: 0');
443
+
444
+		// Microsoft clients default to the Frontpage protocol
445
+		// unless we tell them to use WebDAV
446
+		header('MS-Author-Via: DAV');
447
+
448
+		$this->http_status('200 OK');
449
+	}
450
+
451
+	// }}}
452
+
453
+	// {{{ propfind_request_helper
454
+
455
+	/**
456
+	 * PROPFIND request helper - prepares data-structures from PROPFIND requests
457
+	 *
458
+	 * @param options
459
+	 * @return void
460
+	 */
461
+	function propfind_request_helper(&$options)
462
+	{
463
+		$options = array();
464
+		$options['path'] = $this->path;
465
+
466
+		// get depth from header (default is 'infinity')
467
+		$options['depth'] = 'infinity';
468
+		if (isset($_SERVER['HTTP_DEPTH'])) {
469
+			$options['depth'] = $_SERVER['HTTP_DEPTH'];
470
+		}
471
+
472
+		// analyze request payload
473
+		$parser = new _parse_propfind('php://input');
474
+		if (!$parser->success) {
475
+			$this->http_status('400 Bad Request');
476
+			return;
477
+		}
478
+
479
+		$options['props'] = $parser->props;
480
+
481
+		return true;
482
+	}
483
+
484
+	// }}}
485
+
486
+	// {{{ propfind_response_helper
487
+
488
+	/**
489
+	 * PROPFIND response helper - format PROPFIND response
490
+	 *
491
+	 * @param options
492
+	 * @param files
493
+	 * @return void
494
+	 */
495
+	function propfind_response_helper($options, $files)
496
+	{
497
+		$responses = array();
498
+
499
+		// now loop over all returned files
500
+		foreach ($files as $file) {
501
+
502
+			// collect namespaces here
503
+			// Microsoft need this special namespace for date and time values
504
+			$ns_hash = array(
505
+				'urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882' => 'ns0');
506
+
507
+			$response = array();
508
+
509
+			$response['href'] = $this->getHref($file['path']);
510
+			if (isset($file['href'])) {
511
+				$response['href'] = $file['href'];
512
+			}
513
+
514
+			$response['propstat'] = array();
515
+
516
+			if (is_array($options['props'])) {
517
+
518
+				// loop over all requested properties
519
+				foreach ($options['props'] as $reqprop) {
520
+					$status = '200 OK';
521
+					$prop = $this->getProp($reqprop, $file, $options);
522
+
523
+					if (isset($prop['status'])) {
524
+						$status = $prop['status'];
525
+					}
526
+
527
+					if (!isset($response['propstat'][$status])) {
528
+						$response['propstat'][$status] = array();
529
+					}
530
+
531
+					$response['propstat'][$status][] = $prop;
532
+
533
+					// namespace handling
534
+					if (empty($prop['ns']) || // empty namespace
535
+							$prop['ns'] == 'DAV:' || // default namespace
536
+							isset($ns_hash[$prop['ns']])) { // already known
537
+						continue;
538
+					}
539
+
540
+					// register namespace
541
+					$ns_hash[$prop['ns']] = 'ns' . count($ns_hash);
542
+				}
543
+			} else if (is_array($file['props'])) {
544
+
545
+				// loop over all returned properties
546
+				foreach ($file['props'] as $prop) {
547
+					$status = '200 OK';
548
+
549
+					if (isset($prop['status'])) {
550
+						$status = $prop['status'];
551
+					}
552
+
553
+					if (!isset($response['propstat'][$status])) {
554
+						$response['propstat'][$status] = array();
555
+					}
556
+
557
+					if ($options['props'] == 'propname') {
558
+
559
+						// only names of all existing properties were requested
560
+						// so remove values
561
+						unset($prop['value']);
562
+					}
563
+
564
+					$response['propstat'][$status][] = $prop;
565
+						unset($prop['value']);
566
+
567
+					// namespace handling
568
+					if (empty($prop['ns']) || // empty namespace
569
+							$prop['ns'] == 'DAV:' || // default namespace
570
+							isset($ns_hash[$prop['ns']])) { // already known
571
+						continue;
572
+					}
573
+
574
+					// register namespace
575
+					$ns_hash[$prop['ns']] = 'ns' . count($ns_hash);
576
+				}
577
+			}
578
+
579
+			$response['ns_hash'] = $ns_hash;
580
+			$responses[] = $response;
581
+		}
582
+
583
+		$this->_multistatus($responses);
584
+	}
585
+
586
+	function _multistatus($responses)
587
+	{
588
+		// now we generate the response header...
589
+		$this->http_status('207 Multi-Status');
590
+		header('Content-Type: text/xml; charset="utf-8"');
591
+
592
+		// ...and payload
593
+		echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
594
+		echo "<D:multistatus xmlns:D=\"DAV:\">\n";
595
+
596
+		foreach ($responses as $response) {
597
+
598
+			// ignore empty or incomplete entries
599
+			if (!is_array($response) || empty($response)) {
600
+				continue;
601
+			}
602
+
603
+			$ns_defs = array();
604
+			foreach ($response['ns_hash'] as $name => $prefix) {
605
+				$ns_defs[] = "xmlns:$prefix=\"$name\"";
606
+			}
607
+			echo ' <D:response ' . implode(' ', $ns_defs) . ">\n";
608
+			echo "  <D:href>$response[href]</D:href>\n";
609
+
610
+			// report all found properties and their values (if any)
611
+			// nothing to do if no properties were returend for a file
612
+			if (isset($response['propstat']) &&
613
+					is_array($response['propstat'])) {
614
+
615
+				foreach ($response['propstat'] as $status => $props) {
616
+					echo "  <D:propstat>\n";
617
+					echo "   <D:prop>\n";
618
+
619
+					foreach ($props as $prop) {
620
+						if (!is_array($prop) || !isset($prop['name'])) {
621
+							continue;
622
+						}
623
+
624
+						// empty properties (cannot use empty for check as '0'
625
+						// is a legal value here)
626
+						if (!isset($prop['value']) || empty($prop['value']) &&
627
+								$prop['value'] !== 0) {
628
+							if ($prop['ns'] == 'DAV:') {
629
+								echo "    <D:$prop[name]/>\n";
630
+								continue;
631
+							}
632
+
633
+							if (!empty($prop['ns'])) {
634
+								echo '    <' .
635
+									$response['ns_hash'][$prop['ns']] .
636
+									":$prop[name]/>\n";
637
+								continue;
638
+							}
639
+
640
+							echo "    <$prop[name] xmlns=\"\"/>";
641
+							continue;
642
+						}
643
+
644
+						// some WebDAV properties need special treatment
645
+						if ($prop['ns'] == 'DAV:') {
646
+
647
+							switch ($prop['name']) {
648
+							case 'creationdate':
649
+								echo "    <D:creationdate ns0:dt=\"dateTime.tz\">\n";
650
+								echo '     ' . gmdate('Y-m-d\TH:i:s\Z', $prop['value']) . "\n";
651
+								echo "    </D:creationdate>\n";
652
+								break;
653
+
654
+							case 'getlastmodified':
655
+								echo "    <D:getlastmodified ns0:dt=\"dateTime.rfc1123\">\n";
656
+								echo '     ' . gmdate('D, d M Y H:i:s', $prop['value']) . " GMT\n";
657
+								echo "    </D:getlastmodified>\n";
658
+								break;
659
+
660
+							case 'resourcetype':
661
+								echo "    <D:resourcetype>\n";
662
+								echo "     <D:$prop[value]/>\n";
663
+								echo "    </D:resourcetype>\n";
664
+								break;
665
+
666
+							case 'supportedlock':
667
+
668
+								if (is_array($prop[value])) {
669
+									$prop[value] = $this->_lockentries($prop[value]);
670
+								}
671
+								echo "    <D:supportedlock>\n";
672
+								echo "     $prop[value]\n";
673
+								echo "    </D:supportedlock>\n";
674
+								break;
675
+
676
+							case 'lockdiscovery':
677
+
678
+								if (is_array($prop[value])) {
679
+									$prop[value] = $this->_activelocks($prop[value]);
680
+								}
681
+								echo "    <D:lockdiscovery>\n";
682
+								echo "     $prop[value]\n";
683
+								echo "    </D:lockdiscovery>\n";
684
+								break;
685
+
686
+							default:
687
+								echo "    <D:$prop[name]>\n";
688
+								echo '     ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n";
689
+								echo "    </D:$prop[name]>\n";
690
+							}
691
+
692
+							continue;
693
+						}
694
+
695
+						if (!empty($prop['ns'])) {
696
+							echo '    <' . $response['ns_hash'][$prop['ns']] . ":$prop[name]>\n";
697
+							echo '     ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n";
698
+							echo '    </' . $response['ns_hash'][$prop['ns']] . ":$prop[name]>\n";
699
+
700
+							continue;
701
+						}
702
+
703
+						echo "    <$prop[name] xmlns=\"\">\n";
704
+						echo '     ' . $this->_prop_encode(htmlspecialchars($prop['value'])) . "\n";
705
+						echo "    </$prop[name]>\n";
706
+					}
707
+
708
+					echo "   </D:prop>\n";
709
+					echo "   <D:status>HTTP/1.1 $status</D:status>\n";
710
+					echo "  </D:propstat>\n";
711
+				}
712
+			}
713
+
714
+			if (isset($response['status'])) {
715
+				echo "  <D:status>HTTP/1.1 $status</D:status>\n";
716
+			}
717
+
718
+			if (isset($response['responsedescription'])) {
719
+				echo "  <D:responsedescription>\n";
720
+				echo '   ' . $this->_prop_encode(htmlspecialchars($response['responsedescription'])) . "\n";
721
+				echo "  </D:responsedescription>\n";
722
+			}
723
+
724
+			echo " </D:response>\n";
725
+		}
726
+
727
+		echo "</D:multistatus>\n";
728
+	}
729
+
730
+	// }}}
731
+
732
+	// {{{ propfind_wrapper
733
+
734
+	/**
735
+	 * PROPFIND method wrapper
736
+	 *
737
+	 * @param void
738
+	 * @return void
739
+	 */
740
+	function propfind_wrapper()
741
+	{
742
+		// prepare data-structure from PROPFIND request
743
+		if (!$this->propfind_request_helper($options)) {
744
+			return;
745
+		}
746
+
747
+		// call user handler
748
+		if (!$this->propfind($options, $files)) {
749
+			return;
750
+		}
751
+
752
+		// format PROPFIND response
753
+		$this->propfind_response_helper($options, $files);
754
+	}
755
+
756
+	// }}}
757
+
758
+	// {{{ proppatch_request_helper
759
+
760
+	/**
761
+	 * PROPPATCH request helper - prepares data-structures from PROPPATCH requests
762
+	 *
763
+	 * @param options
764
+	 * @return void
765
+	 */
766
+	function proppatch_request_helper(&$options)
767
+	{
768
+		$options = array();
769
+		$options['path'] = $this->path;
770
+
771
+		$propinfo = new _parse_proppatch('php://input');
772
+
773
+		if (!$propinfo->success) {
774
+			$this->http_status('400 Bad Request');
775
+			return;
776
+		}
777
+
778
+		$options['props'] = $propinfo->props;
779
+
780
+		return true;
781
+	}
782
+
783
+	// }}}
784
+
785
+	// {{{ proppatch_response_helper
786
+
787
+	/**
788
+	 * PROPPATCH response helper - format PROPPATCH response
789
+	 *
790
+	 * @param options
791
+	 * @param responsedescr
792
+	 * @return void
793
+	 */
794
+	function proppatch_response_helper($options, $responsedescription=null)
795
+	{
796
+		$response = array();
797
+
798
+		$response['href'] = $this->getHref($options['path']);
799
+		if (isset($options['href'])) {
800
+			$response['href'] = $options['href'];
801
+		}
802
+
803
+		$response['propstat'] = array();
804
+
805
+		// collect namespaces here
806
+		$ns_hash = array();
807
+
808
+		if (isset($options['props']) && is_array($options['props'])) {
809
+			foreach ($options['props'] as $prop) {
810
+				$status = '200 OK';
811
+				if (isset($prop['status'])) {
812
+					$status = $prop['status'];
813
+				}
814
+
815
+				if (!isset($response['propstat'][$status])) {
816
+					$response['propstat'][$status] = array();
817
+				}
818
+
819
+				$response['propstat'][$status][] = $prop;
820
+
821
+				// namespace handling
822
+				if (empty($prop['ns']) || // empty namespace
823
+						$prop['ns'] == 'DAV:' || // default namespace
824
+						isset($ns_hash[$prop['ns']])) { // already known
825
+					continue;
826
+				}
827
+
828
+				// register namespace
829
+				$ns_hash[$prop['ns']] = 'ns' . count($ns_hash);
830
+			}
831
+		}
832
+
833
+		$response['ns_hash'] = $ns_hash;
834
+		$response['responsedescription'] = $responsedescription;
835
+
836
+		$this->_multistatus(array($response));
837
+	}
838
+
839
+	// }}}
840
+
841
+	// {{{ proppatch_wrapper
842
+
843
+	/**
844
+	 * PROPPATCH method wrapper
845
+	 *
846
+	 * @param void
847
+	 * @return void
848
+	 */
849
+	function proppatch_wrapper()
850
+	{
851
+		// check resource is not locked
852
+		if (!$this->check_lock_wrapper($this->path)) {
853
+			$this->http_status('423 Locked');
854
+			return;
855
+		}
856
+
857
+		// perpare data-structure from PROPATCH request
858
+		if (!$this->proppatch_request_helper($options)) {
859
+			return;
860
+		}
861
+
862
+		// call user handler
863
+		$responsedescription = $this->proppatch($options);
864
+
865
+		// format PROPPATCH response
866
+		$this->proppatch_response_helper($options, $responsedescription);
867
+	}
868
+
869
+	// }}}
870
+
871
+	// {{{ mkcol_wrapper
872
+
873
+	/**
874
+	 * MKCOL method wrapper
875
+	 *
876
+	 * @param void
877
+	 * @return void
878
+	 */
879
+	function mkcol_wrapper()
880
+	{
881
+		$options = array();
882
+		$options['path'] = $this->path;
883
+
884
+		$status = $this->mkcol($options);
885
+
886
+		$this->http_status($status);
887
+	}
888
+
889
+	// }}}
890
+
891
+	// {{{ get_request_helper
892
+
893
+	/**
894
+	 * GET request helper - prepares data-structures from GET requests
895
+	 *
896
+	 * @param options
897
+	 * @return void
898
+	 */
899
+	function get_request_helper(&$options)
900
+	{
901
+		// TODO check for invalid stream
902
+
903
+		$options = array();
904
+		$options['path'] = $this->path;
905
+
906
+		$this->_get_ranges($options);
907
+
908
+		return true;
909
+	}
910
+
911
+	/**
912
+	 * parse HTTP Range: header
913
+	 *
914
+	 * @param  array options array to store result in
915
+	 * @return void
916
+	 */
917
+	function _get_ranges(&$options)
918
+	{
919
+		// process Range: header if present
920
+		if (isset($_SERVER['HTTP_RANGE'])) {
921
+
922
+			// we only support standard 'bytes' range specifications for now
923
+			if (ereg('bytes[[:space:]]*=[[:space:]]*(.+)', $_SERVER['HTTP_RANGE'], $matches)) {
924
+				$options['ranges'] = array();
925
+
926
+				// ranges are comma separated
927
+				foreach (explode(',', $matches[1]) as $range) {
928
+					// ranges are either from-to pairs or just end positions
929
+					list($start, $end) = explode('-', $range);
930
+					$options['ranges'][] = ($start === '')
931
+						? array('last' => $end)
932
+						: array('start' => $start, 'end' => $end);
933
+				}
934
+			}
935
+		}
936
+	}
937
+
938
+	// }}}
939
+
940
+	// {{{ get_response_helper
941
+
942
+	/**
943
+	 * GET response helper - format GET response
944
+	 *
945
+	 * @param options
946
+	 * @param status
947
+	 * @return void
948
+	 */
949
+	function get_response_helper($options, $status)
950
+	{
951
+		if (empty($status)) {
952
+			$status = '404 Not Found';
953
+		}
954
+
955
+		// set headers before we start printing
956
+		$this->http_status($status);
957
+
958
+		if ($status !== true) {
959
+			return;
960
+		}
961
+
962
+		if (!isset($options['mimetype'])) {
963
+			$options['mimetype'] = 'application/octet-stream';
964
+		}
965
+		header("Content-Type: $options[mimetype]");
966
+
967
+		if (isset($options['mtime'])) {
968
+			header('Last-Modified:' .
969
+				gmdate('D, d M Y H:i:s', $options['mtime']) . 'GMT');
970
+		}
971
+
972
+		if ($options['stream']) {
973
+			// GET handler returned a stream
974
+
975
+			if (!empty($options['ranges']) &&
976
+					(fseek($options['stream'], 0, SEEK_SET) === 0)) {
977
+				// partial request and stream is seekable
978
+
979
+				if (count($options['ranges']) === 1) {
980
+					$range = $options['ranges'][0];
981
+
982
+					if (isset($range['start'])) {
983
+						fseek($options['stream'], $range['start'], SEEK_SET);
984
+						if (feof($options['stream'])) {
985
+							$this->http_status('416 Requested Range Not Satisfiable');
986
+							return;
987
+						}
988
+
989
+						if (isset($range['end'])) {
990
+							$size = $range['end'] - $range['start'] + 1;
991
+							$this->http_status('206 Partial');
992
+							header("Content-Length: $size");
993
+							header("Content-Range: $range[start]-$range[end]/" .
994
+								(isset($options['size']) ? $options['size'] : '*'));
995
+							while ($size && !feof($options['stream'])) {
996
+								$buffer = fread($options['stream'], 4096);
997
+								$size -= strlen($buffer);
998
+								echo $buffer;
999
+							}
1000
+						} else {
1001
+							$this->http_status('206 Partial');
1002
+							if (isset($options['size'])) {
1003
+								header("Content-Length: " .
1004
+									($options['size'] - $range['start']));
1005
+								header("Content-Range: $start-$end/" .
1006
+									(isset($options['size']) ? $options['size'] : '*'));
1007
+							}
1008
+							fpassthru($options['stream']);
1009
+						}
1010
+					} else {
1011
+						header("Content-Length: $range[last]");
1012
+						fseek($options['stream'], -$range['last'], SEEK_END);
1013
+						fpassthru($options['stream']);
1014
+					}
1015
+				} else {
1016
+					$this->_multipart_byterange_header(); // init multipart
1017
+					foreach ($options['ranges'] as $range) {
1018
+
1019
+						// TODO what if size unknown? 500?
1020
+						if (isset($range['start'])) {
1021
+							$from = $range['start'];
1022
+							$to = !empty($range['end']) ? $range['end'] : $options['size'] - 1;
1023
+						} else {
1024
+							$from = $options['size'] - $range['last'] - 1;
1025
+							$to = $options['size'] - 1;
1026
+						}
1027
+						$total = isset($options['size']) ? $options['size'] : '*';
1028
+						$size = $to - $from + 1;
1029
+						$this->_multipart_byterange_header($options['mimetype'],
1030
+							$from, $to, $total);
1031
+
1032
+						fseek($options['stream'], $start, SEEK_SET);
1033
+						while ($size && !feof($options['stream'])) {
1034
+							$buffer = fread($options['stream'], 4096);
1035
+							$size -= strlen($buffer);
1036
+							echo $buffer;
1037
+						}
1038
+					}
1039
+					$this->_multipart_byterange_header(); // end multipart
1040
+				}
1041
+			} else {
1042
+				// normal request or stream isn't seekable, return full content
1043
+				if (isset($options['size'])) {
1044
+					header("Content-Length: $options[size]");
1045
+				}
1046
+
1047
+				fpassthru($options['stream']);
1048
+			}
1049
+		} else if (isset($options['data']))  {
1050
+			if (is_array($options['data'])) {
1051
+				// reply to partial request
1052
+			} else {
1053
+				header("Content-Length: " . strlen($options['data']));
1054
+				echo $options['data'];
1055
+			}
1056
+		}
1057
+	}
1058
+
1059
+	/**
1060
+	 * generate separator headers for multipart response
1061
+	 *
1062
+	 * first and last call happen without parameters to generate
1063
+	 * the initial header and closing sequence, all calls inbetween
1064
+	 * require content mimetype, start and end byte position and
1065
+	 * optionaly the total byte length of the requested resource
1066
+	 *
1067
+	 * @param  string  mimetype
1068
+	 * @param  int     start byte position
1069
+	 * @param  int     end   byte position
1070
+	 * @param  int     total resource byte size
1071
+	 */
1072
+	function _multipart_byterange_header($mimetype = false, $from = false,
1073
+		$to = false, $total = false)
1074
+	{
1075
+		if ($mimetype === false) {
1076
+			if (!isset($this->multipart_separator)) {
1077
+				// init
1078
+				// a little naive, this sequence *might* be part of the content
1079
+				// but it's really not likely and rather expensive to check
1080
+				$this->multipart_separator = 'SEPARATOR_' . md5(microtime());
1081
+
1082
+				// generate HTTP header
1083
+				header('Content-Type: multipart/byteranges; boundary=' .
1084
+					$this->multipart_separator);
1085
+
1086
+				return;
1087
+			}
1088
+
1089
+			// end
1090
+			// generate closing multipart sequence
1091
+			echo "\n--{$this->multipart_separator}--";
1092
+
1093
+			return;
1094
+		}
1095
+
1096
+		// generate separator and header for next part
1097
+		echo "\n--{$this->multipart_separator}\n";
1098
+		echo "Content-Type: $mimetype\n";
1099
+		echo "Content-Range: $from-$to/"
1100
+			. ($total === false ? "*" : $total) . "\n\n";
1101
+	}
1102
+
1103
+	// }}}
1104
+
1105
+	// {{{ get_wrapper
1106
+
1107
+	/**
1108
+	 * GET method wrapper
1109
+	 *
1110
+	 * @param void
1111
+	 * @return void
1112
+	 */
1113
+	function get_wrapper()
1114
+	{
1115
+		// perpare data-structure from GET request
1116
+		if (!$this->get_request_helper($options)) {
1117
+			return;
1118
+		}
1119
+
1120
+		// call user handler
1121
+		$status = $this->get($options);
1122
+
1123
+		// format GET response
1124
+		$this->get_response_helper($options, $status);
1125
+	}
1126
+
1127
+	// }}}
1128
+
1129
+	// {{{ head_response_helper
1130
+
1131
+	/**
1132
+	 * HEAD response helper - format HEAD response
1133
+	 *
1134
+	 * @param options
1135
+	 * @param status
1136
+	 * @return void
1137
+	 */
1138
+	function head_response_helper($options, $status)
1139
+	{
1140
+		if (empty($status)) {
1141
+			$status('404 Not Found');
1142
+		}
1143
+
1144
+		// set headers before we start printing
1145
+		$this->http_status($status);
1146
+
1147
+		if ($status !== true) {
1148
+			return;
1149
+		}
1150
+
1151
+		if (!isset($options['mimetype'])) {
1152
+			$options['mimetype'] = 'application/octet-stream';
1153
+		}
1154
+		header("Content-Type: $options[mimetype]");
1155
+
1156
+		if (isset($options['mtime'])) {
1157
+			header('Last-Modified:' .
1158
+				gmdate('D, d M Y H:i:s', $options['mtime']) . 'GMT');
1159
+		}
1160
+
1161
+		if (isset($options['stream'])) {
1162
+			// GET handler returned a stream
1163
+
1164
+			if (!empty($options['ranges']) &&
1165
+					(fseek($options['stream'], 0, SEEK_SET) === 0)) {
1166
+				// partial request and stream is seekable
1167
+
1168
+				if (count($options['ranges']) === 1) {
1169
+					$range = $options['ranges'][0];
1170
+
1171
+					if (isset($range['start'])) {
1172
+						fseek($options['stream'], $range['start'], SEEK_SET);
1173
+						if (feof($options['stream'])) {
1174
+							$this->http_status('416 Requested Range Not Satisfiable');
1175
+							return;
1176
+						}
1177
+
1178
+						if (isset($range['end'])) {
1179
+							$size = $range['end'] - $range['start'] + 1;
1180
+							$this->http_status('206 Partial');
1181
+							header("Content-Length: $size");
1182
+							header("Content-Range: $range[start]-$range[end]/" .
1183
+								(isset($options['size']) ? $options['size'] : '*'));
1184
+						} else {
1185
+							$this->http_status('206 Partial');
1186
+							if (isset($options['size'])) {
1187
+								header("Content-Length: " .
1188
+									($options['size'] - $range['start']));
1189
+								header("Content-Range: $start-$end/" .
1190
+									(isset($options['size']) ? $options['size'] : '*'));
1191
+							}
1192
+						}
1193
+					} else {
1194
+						header("Content-Length: $range[last]");
1195
+						fseek($options['stream'], -$range['last'], SEEK_END);
1196
+					}
1197
+				} else {
1198
+					$this->_multipart_byterange_header(); // init multipart
1199
+					foreach ($options['ranges'] as $range) {
1200
+
1201
+						// TODO what if size unknown? 500?
1202
+						if (isset($range['start'])) {
1203
+							$from = $range['start'];
1204
+							$to = !empty($range['end']) ? $range['end'] :
1205
+								$options['size'] - 1;
1206
+						} else {
1207
+							$from = $options['size'] - $range['last'] - 1;
1208
+							$to = $options['size'] - 1;
1209
+						}
1210
+						$total = isset($options['size']) ? $options['size'] :
1211
+							'*';
1212
+						$size = $to - $from + 1;
1213
+						$this->_multipart_byterange_header($options['mimetype'],
1214
+							$from, $to, $total);
1215
+
1216
+						fseek($options['stream'], $start, SEEK_SET);
1217
+					}
1218
+					$this->_multipart_byterange_header(); // end multipart
1219
+				}
1220
+			} else {
1221
+				// normal request or stream isn't seekable, return full content
1222
+				if (isset($options['size'])) {
1223
+					header("Content-Length: $options[size]");
1224
+				}
1225
+			}
1226
+		} else if (isset($options['data']))  {
1227
+			if (is_array($options['data'])) {
1228
+				// reply to partial request
1229
+			} else {
1230
+				header("Content-Length: " . strlen($options['data']));
1231
+			}
1232
+		}
1233
+	}
1234
+
1235
+	// }}}
1236
+
1237
+	// {{{ head_wrapper
1238
+
1239
+	/**
1240
+	 * HEAD method wrapper
1241
+	 *
1242
+	 * @param void
1243
+	 * @return void
1244
+	 */
1245
+	function head_wrapper()
1246
+	{
1247
+		$options = array();
1248
+		$options['path'] = $this->path;
1249
+
1250
+		// call user handler
1251
+		if (method_exists($this, 'head')) {
1252
+			$status = $this->head($options);
1253
+		} else {
1254
+
1255
+			// can emulate HEAD using GET
1256
+			ob_start();
1257
+			$status = $this->get($options);
1258
+			ob_end_clean();
1259
+		}
1260
+
1261
+		// format HEAD response
1262
+		$this->head_response_helper($options, $status);
1263
+	}
1264
+
1265
+	// }}}
1266
+
1267
+	// {{{ put_request_helper
1268
+
1269
+	/**
1270
+	 * PUT request helper - prepares data-structures from PUT requests
1271
+	 *
1272
+	 * @param options
1273
+	 * @return void
1274
+	 */
1275
+	function put_request_helper(&$options)
1276
+	{
1277
+		$options = array();
1278
+		$options['path'] = $this->path;
1279
+		$options['content_length'] = $_SERVER['CONTENT_LENGTH'];
1280
+
1281
+		// get the content-type
1282
+		if (isset($_SERVER['CONTENT_TYPE'])) {
1283
+
1284
+			// for now we do not support any sort of multipart requests
1285
+			if (!strncmp($_SERVER['CONTENT_TYPE'], 'multipart/', 10)) {
1286
+				$this->http_status('501 Not Implemented');
1287
+				echo 'The service does not support mulipart PUT requests';
1288
+				return;
1289
+			}
1290
+
1291
+			$options['content_type'] = $_SERVER['CONTENT_TYPE'];
1292
+		} else {
1293
+
1294
+			// default content type if none given
1295
+			$options['content_type'] = 'application/unknown';
1296
+		}
1297
+
1298
+		// RFC2616 2.6 says: The recipient of the entity MUST NOT
1299
+		// ignore any Content-* (e.g. Content-Range) headers that it
1300
+		// does not understand or implement and MUST return a 501
1301
+		// (Not Implemented) response in such cases.
1302
+		foreach ($_SERVER as $key => $value) {
1303
+			if (strncmp($key, 'HTTP_CONTENT', 11)) continue;
1304
+			switch ($key) {
1305
+			case 'HTTP_CONTENT_ENCODING': // RFC2616 14.11
1306
+
1307
+				// TODO support this if ext/zlib filters are available
1308
+				$this->http_status('501 Not Implemented');
1309
+				echo "The service does not support '$value' content encoding";
1310
+				return;
1311
+
1312
+			case 'HTTP_CONTENT_LANGUAGE': // RFC2616 14.12
1313
+
1314
+				// we assume it is not critical if this one is ignored
1315
+				// in the actual PUT implementation...
1316
+				$options['content_language'] = $value;
1317
+				break;
1318
+
1319
+			case 'HTTP_CONTENT_LOCATION': // RFC2616 14.14
1320
+
1321
+				// The meaning of the Content-Location header in PUT
1322
+				// or POST requests is undefined; servers are free
1323
+				// to ignore it in those cases. */
1324
+				break;
1325
+
1326
+			case 'HTTP_CONTENT_RANGE': // RFC2616 14.16
1327
+
1328
+				// single byte range requests are supported
1329
+				// the header format is also specified in RFC2616 14.16
1330
+				// TODO we have to ensure that implementations support this or send 501 instead
1331
+				if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $value, $matches)) {
1332
+					$this->http_status('400 Bad Request');
1333
+					echo 'The service does only support single byte ranges';
1334
+					return;
1335
+				}
1336
+
1337
+				$range = array('start' => $matches[1], 'end' => $matches[2]);
1338
+				if (is_numeric($matches[3])) {
1339
+					$range['total_length'] = $matches[3];
1340
+				}
1341
+				$option['ranges'][] = $range;
1342
+
1343
+				// TODO make sure the implementation supports partial PUT
1344
+				// this has to be done in advance to avoid data being overwritten
1345
+				// on implementations that do not support this...
1346
+				break;
1347
+
1348
+			case 'HTTP_CONTENT_MD5': // RFC2616 14.15
1349
+
1350
+				// TODO maybe we can just pretend here?
1351
+				$this->http_status('501 Not Implemented');
1352
+				echo 'The service does not support content MD5 checksum verification';
1353
+				return;
1354
+
1355
+			default:
1356
+
1357
+				// any other unknown Content-* headers
1358
+				$this->http_status('501 Not Implemented');
1359
+				echo "The service does not support '$key'";
1360
+				return;
1361
+			}
1362
+		}
1363
+
1364
+		$options['stream'] = fopen('php://input', 'r');
1365
+
1366
+		return true;
1367
+	}
1368
+
1369
+	// }}}
1370
+
1371
+	// {{{ put_response_helper
1372
+
1373
+	/**
1374
+	 * PUT response helper - format PUT response
1375
+	 *
1376
+	 * @param options
1377
+	 * @param status
1378
+	 * @return void
1379
+	 */
1380
+	function put_response_helper($options, $status)
1381
+	{
1382
+		if (empty($status)) {
1383
+			$status = '403 Forbidden';
1384
+		} else if (is_resource($status) &&
1385
+				get_resource_type($status) == 'stream') {
1386
+			$stream = $status;
1387
+			$status = $options['new'] === false ? '204 No Content' :
1388
+				'201 Created';
1389
+
1390
+			if (!empty($options['ranges'])) {
1391
+
1392
+				// TODO multipart support is missing (see also above)
1393
+				if (0 == fseek($stream, $range[0]['start'], SEEK_SET)) {
1394
+					$length = $range[0]['end'] - $range[0]['start'] + 1;
1395
+					if (!fwrite($stream, fread($options['stream'], $length))) {
1396
+						$status = '403 Forbidden';
1397
+					}
1398
+				} else {
1399
+					$status = '403 Forbidden';
1400
+				}
1401
+			} else {
1402
+				while (!feof($options['stream'])) {
1403
+					$buf = fread($options['stream'], 4096);
1404
+					if (fwrite($stream, $buf) != 4096) {
1405
+						break;
1406
+					}
1407
+				}
1408
+			}
1409
+
1410
+			fclose($stream);
1411
+		}
1412
+
1413
+		$this->http_status($status);
1414
+	}
1415
+
1416
+	// }}}
1417
+
1418
+	// {{{ put_wrapper
1419
+
1420
+	/**
1421
+	 * PUT method wrapper
1422
+	 *
1423
+	 * @param void
1424
+	 * @return void
1425
+	 */
1426
+	function put_wrapper()
1427
+	{
1428
+		// check resource is not locked
1429
+		if (!$this->check_lock_wrapper($this->path)) {
1430
+			$this->http_status('423 Locked');
1431
+			return;
1432
+		}
1433
+
1434
+		// perpare data-structure from PUT request
1435
+		if (!$this->put_request_helper($options)) {
1436
+			return;
1437
+		}
1438
+
1439
+		// call user handler
1440
+		$status = $this->put($options);
1441
+
1442
+		// format PUT response
1443
+		$this->put_response_helper($options, $status);
1444
+	}
1445
+
1446
+	// }}}
1447
+
1448
+	// {{{ delete_wrapper
1449
+
1450
+	/**
1451
+	 * DELETE method wrapper
1452
+	 *
1453
+	 * @param void
1454
+	 * @return void
1455
+	 */
1456
+	function delete_wrapper()
1457
+	{
1458
+		// RFC2518 9.2 last paragraph
1459
+		if (isset($_SERVER['HTTP_DEPTH']) &&
1460
+				$_SERVER['HTTP_DEPTH'] != 'infinity') {
1461
+			$this->http_status('400 Bad Request');
1462
+			return;
1463
+		}
1464
+
1465
+		// check resource is not locked
1466
+		if (!$this->check_lock_wrapper($this->path)) {
1467
+			$this->http_status('423 Locked');
1468
+			return;
1469
+		}
1470
+
1471
+		$options = array();
1472
+		$options['path'] = $this->path;
1473
+
1474
+		// call user handler
1475
+		$status = $this->delete($options);
1476
+		if ($status === true) {
1477
+			$status = '204 No Content';
1478
+		}
1479
+
1480
+		$this->http_status($status);
1481
+	}
1482
+
1483
+	// }}}
1484
+
1485
+	// {{{ copymove_request_helper
1486
+
1487
+	/**
1488
+	 * COPY/MOVE request helper - prepares data-structures from COPY/MOVE
1489
+	 * requests
1490
+	 *
1491
+	 * @param options
1492
+	 * @return void
1493
+	 */
1494
+	function copymove_request_helper(&$options)
1495
+	{
1496
+		$options = array();
1497
+		$options['path'] = $this->path;
1498
+
1499
+		$options['depth'] = 'infinity';
1500
+		if (isset($_SERVER['HTTP_DEPTH'])) {
1501
+			$options['depth'] = $_SERVER['HTTP_DEPTH'];
1502
+		}
1503
+
1504
+		// RFC2518 9.6, 8.8.4 and 8.9.3
1505
+		$options['overwrite'] = true;
1506
+		if (isset($_SERVER['HTTP_OVERWRITE'])) {
1507
+			$options['overwrite'] = $_SERVER['HTTP_OVERWRITE'] == 'T';
1508
+		}
1509
+
1510
+		list ($src_host, $src_port) = explode(':', $_SERVER['HTTP_HOST']);
1511
+		if (empty($src_port)) {
1512
+			$src_port = 80;
1513
+		}
1514
+
1515
+		$dst_url = parse_url($_SERVER['HTTP_DESTINATION']);
1516
+		$dst_host = $dst_url['host'];
1517
+
1518
+		$dst_port = $dst_url['port'];
1519
+		if (empty($dst_port)) {
1520
+			$dst_port = 80;
1521
+		}
1522
+
1523
+		$dst_path = $dst_url['path'];
1524
+
1525
+		// base_uri is urldecoded
1526
+		$dst_path = $this->_urldecode($dst_path);
1527
+
1528
+		// does the destination resource belong on this server?
1529
+		if ($dst_host == $src_host && $dst_port == $src_port &&
1530
+				!strncmp($dst_path, $this->base_uri, strlen($this->base_uri))) {
1531
+			$options['dest'] = substr($dst_path, strlen($this->base_uri));
1532
+
1533
+			$options['dest'] = ltrim($options['dest'], '/');
1534
+
1535
+			// check source & destination are not the same - data could be lost
1536
+			// if overwrite is true - RFC2518 8.8.5
1537
+			if ($options['dest'] == $this->path) {
1538
+				$this->http_status('403 Forbidden');
1539
+				return;
1540
+			}
1541
+
1542
+			return true;
1543
+		}
1544
+
1545
+		$options['dest_url'] = $_SERVER['HTTP_DESTINATION'];
1546
+
1547
+		return true;
1548
+	}
1549
+
1550
+	// }}}
1551
+
1552
+	// {{{ copy_wrapper
1553
+
1554
+	/**
1555
+	 * COPY method wrapper
1556
+	 *
1557
+	 * @param void
1558
+	 * @return void
1559
+	 */
1560
+	function copy_wrapper()
1561
+	{
1562
+		// no need to check source is not locked
1563
+
1564
+		// perpare data-structure from COPY request
1565
+		if (!$this->copymove_request_helper($options)) {
1566
+			return;
1567
+		}
1568
+
1569
+		// check destination is not locked
1570
+		if (isset($options['dest']) &&
1571
+				!$this->check_lock_wrapper($options['dest'])) {
1572
+			$this->http_status('423 Locked');
1573
+			return;
1574
+		}
1575
+
1576
+		// call user handler
1577
+		$status = $this->copy($options);
1578
+		if ($status === true) {
1579
+			$status = $options['new'] === false ? '204 No Content' :
1580
+				'201 Created';
1581
+		}
1582
+
1583
+		$this->http_status($status);
1584
+	}
1585
+
1586
+	// }}}
1587
+
1588
+	// {{{ move_wrapper
1589
+
1590
+	/**
1591
+	 * MOVE method wrapper
1592
+	 *
1593
+	 * @param void
1594
+	 * @return void
1595
+	 */
1596
+	function move_wrapper()
1597
+	{
1598
+		// check resource is not locked
1599
+		if (!$this->check_lock_wrapper($this->path)) {
1600
+			$this->http_status('423 Locked');
1601
+			return;
1602
+		}
1603
+
1604
+		// perpare data-structure from MOVE request
1605
+		if (!$this->copymove_request_helper($options)) {
1606
+			return;
1607
+		}
1608
+
1609
+		// check destination is not locked
1610
+		if (isset($options['dest']) &&
1611
+				!$this->check_lock_wrapper($options['dest'])) {
1612
+			$this->http_status('423 Locked');
1613
+			return;
1614
+		}
1615
+
1616
+		// call user handler
1617
+		$status = $this->move($options);
1618
+		if ($status === true) {
1619
+			$status = $options['new'] === false ? '204 No Content' :
1620
+				'201 Created';
1621
+		}
1622
+
1623
+		$this->http_status($status);
1624
+	}
1625
+
1626
+	// }}}
1627
+
1628
+	// {{{ lock_request_helper
1629
+
1630
+	/**
1631
+	 * LOCK request helper - prepares data-structures from LOCK requests
1632
+	 *
1633
+	 * @param options
1634
+	 * @return void
1635
+	 */
1636
+	function lock_request_helper(&$options)
1637
+	{
1638
+		$options = array();
1639
+		$options['path'] = $this->path;
1640
+
1641
+		$options['depth'] = 'infinity';
1642
+		if (isset($_SERVER['HTTP_DEPTH'])) {
1643
+			$options['depth'] = $_SERVER['HTTP_DEPTH'];
1644
+		}
1645
+
1646
+		if (isset($_SERVER['HTTP_TIMEOUT'])) {
1647
+			$options['timeout'] = explode(',', $_SERVER['HTTP_TIMEOUT']);
1648
+		}
1649
+
1650
+		if (empty($_SERVER['CONTENT_LENGTH']) && !empty($_SERVER['HTTP_IF'])) {
1651
+
1652
+			// refresh lock
1653
+			$options['update'] = substr($_SERVER['HTTP_IF'], 2, -2);
1654
+
1655
+			return true;
1656
+		}
1657
+
1658
+		// extract lock request information from request XML payload
1659
+		$lockinfo = new _parse_lockinfo('php://input');
1660
+		if (!$lockinfo->success) {
1661
+			$this->http_status('400 Bad Request');
1662
+			return;
1663
+		}
1664
+
1665
+		// new lock
1666
+		$options['scope'] = $lockinfo->lockscope;
1667
+		$options['type']  = $lockinfo->locktype;
1668
+		$options['owner'] = $lockinfo->owner;
1669
+
1670
+		$options['token'] = $this->_new_locktoken();
1671
+
1672
+		return true;
1673
+	}
1674
+
1675
+	// }}}
1676
+
1677
+	// {{{ lock_response_helper
1678
+
1679
+	/**
1680
+	 * LOCK response helper - format LOCK response
1681
+	 *
1682
+	 * @param options
1683
+	 * @param status
1684
+	 * @return void
1685
+	 */
1686
+	function lock_response_helper($options, $status)
1687
+	{
1688
+		if (isset($options['locks']) && is_array($options['locks'])) {
1689
+			$this->http_status('409 Conflict');
1690
+
1691
+			$responses = array();
1692
+			foreach ($options['locks'] as $lock) {
1693
+				$response = array();
1694
+
1695
+				$response['href'] = $this->getHref($lock['path']);
1696
+				if (isset($lock['href'])) {
1697
+					$response['href'] = $lock['href'];
1698
+				}
1699
+
1700
+				$response['status'] = '423 Locked';
1701
+
1702
+				$responses[] = $response;
1703
+			}
1704
+
1705
+			$this->_multistatus($responses);
1706
+			return;
1707
+		}
1708
+
1709
+		if (is_bool($status)) {
1710
+			$status = $status ? '200 OK' : '423 Locked';
1711
+		}
1712
+
1713
+		// set headers before we start printing
1714
+		$this->http_status($status);
1715
+
1716
+		if ($status{0} == 2) { // 2xx states are ok
1717
+			header('Content-Type: text/xml; charset="utf-8"');
1718
+			header("Lock-Token: <$options[token]>");
1719
+
1720
+			echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
1721
+			echo "<D:prop xmlns:D=\"DAV:\">\n";
1722
+			echo " <D:lockdiscovery>\n";
1723
+			echo '  ' . $this->_activelocks(array($options)) . "\n";
1724
+			echo " </D:lockdiscovery>\n";
1725
+			echo "</D:prop>\n";
1726
+		}
1727
+	}
1728
+
1729
+	// }}}
1730
+
1731
+	// {{{ lock_wrapper
1732
+
1733
+	/**
1734
+	 * LOCK method wrapper
1735
+	 *
1736
+	 * @param void
1737
+	 * @return void
1738
+	 */
1739
+	function lock_wrapper()
1740
+	{
1741
+		// perpare data-structure from LOCK request
1742
+		if (!$this->lock_request_helper($options)) {
1743
+			return;
1744
+		}
1745
+
1746
+		// check resource is not locked
1747
+		if (isset($options['update']) &&
1748
+				!$this->check_lock_wrapper($this->path) ||
1749
+				!$this->check_lock_wrapper($this->path,
1750
+				$options['scope'] == 'shared')) {
1751
+			$this->http_status('423 Locked');
1752
+			return;
1753
+		}
1754
+
1755
+		$options['locks'] = $this->getDescendentsLocks($this->path);
1756
+		if (empty($options['locks'])) {
1757
+
1758
+			// call user handler
1759
+			$status = $this->lock($options);
1760
+		}
1761
+
1762
+		// format LOCK response
1763
+		$this->lock_response_helper($options, $status);
1764
+	}
1765
+
1766
+	// }}}
1767
+
1768
+	// {{{ unlock_request_helper
1769
+
1770
+	/**
1771
+	 * UNLOCK request helper - prepares data-structures from UNLOCK requests
1772
+	 *
1773
+	 * @param options
1774
+	 * @return void
1775
+	 */
1776
+	function unlock_request_helper(&$options)
1777
+	{
1778
+		$options = array();
1779
+		$options['path'] = $this->path;
1780
+
1781
+		// strip surrounding <>
1782
+		$options['token'] = substr(trim($_SERVER['HTTP_LOCK_TOKEN']), 1, -1);
1783
+
1784
+		return true;
1785
+	}
1786
+
1787
+	// }}}
1788
+
1789
+	// {{{ unlock_wrapper
1790
+
1791
+	/**
1792
+	 * UNLOCK method wrapper
1793
+	 *
1794
+	 * @param void
1795
+	 * @return void
1796
+	 */
1797
+	function unlock_wrapper()
1798
+	{
1799
+		// perpare data-structure from DELETE request
1800
+		if (!$this->unlock_request_helper($options)) {
1801
+			return;
1802
+		}
1803
+
1804
+		// call user handler
1805
+		$status = $this->unlock($options);
1806
+
1807
+		// RFC2518 8.11.1
1808
+		if ($status === true) {
1809
+			$status = '204 No Content';
1810
+		}
1811
+
1812
+		$this->http_status($status);
1813
+	}
1814
+
1815
+	// }}}
1816
+
1817
+	function getHref($path)
1818
+	{
1819
+		return $this->base_uri . '/' . $path;
1820
+	}
1821
+
1822
+	function getProp($reqprop, $file, $options)
1823
+	{
1824
+		// check if property exists in response
1825
+		foreach ($file['props'] as $prop) {
1826
+			if ($reqprop['name'] == $prop['name'] &&
1827
+					$reqprop['ns'] == $prop['ns']) {
1828
+				return $prop;
1829
+			}
1830
+		}
1831
+
1832
+		if ($reqprop['name'] == 'lockdiscovery' &&
1833
+				$reqprop['ns'] == 'DAV:' &&
1834
+				method_exists($this, 'getLocks')) {
1835
+
1836
+			return $this->mkprop('DAV:', 'lockdiscovery',
1837
+				$this->getLocks($file['path']));
1838
+		}
1839
+
1840
+		// incase the requested property had a value, like calendar-data
1841
+		unset($reqprop['value']);
1842
+		$reqprop['status'] = '404 Not Found';
1843
+		return $reqprop;
1844
+	}
1845
+
1846
+	function getDescendentsLocks($path)
1847
+	{
1848
+		$options = array();
1849
+		$options['path'] = $path;
1850
+		$options['depth'] = 'infinity';
1851
+		$options['props'] = array();
1852
+		$options['props'][] = $this->mkprop('DAV:', 'lockdiscovery', null);
1853
+
1854
+		// call user handler
1855
+		if (!$this->propfind($options, $files)) {
1856
+			return;
1857
+		}
1858
+
1859
+		return $files;
1860
+	}
1861
+
1862
+	// {{{ _allow()
1863
+
1864
+	/**
1865
+	 * list implemented methods
1866
+	 *
1867
+	 * @param void
1868
+	 * @return array something
1869
+	 */
1870
+	function _allow()
1871
+	{
1872
+		// OPTIONS is always there
1873
+		$allow = array('OPTIONS');
1874
+
1875
+		// all other methods need both a method_wrapper() and a method()
1876
+		// implementation
1877
+		// the base class defines only wrappers
1878
+		foreach(get_class_methods($this) as $method) {
1879
+
1880
+			// strncmp breaks with negative len -
1881
+			// http://bugs.php.net/bug.php?id=36944
1882
+			//if (!strncmp('_wrapper', $method, -8)) {
1883
+			if (!strcmp(substr($method, -8), '_wrapper')) {
1884
+				$method = strtolower(substr($method, 0, -8));
1885
+				if (method_exists($this, $method) &&
1886
+						($method != 'lock' && $method != 'unlock' ||
1887
+						method_exists($this, 'getLocks'))) {
1888
+					$allow[] = $method;
1889
+				}
1890
+			}
1891
+		}
1892
+
1893
+		// we can emulate a missing HEAD implemetation using GET
1894
+		if (in_array('GET', $allow)) {
1895
+			$allow[] = 'HEAD';
1896
+		}
1897
+
1898
+		return $allow;
1899
+	}
1900
+
1901
+	// }}}
1902
+
1903
+	// {{{ mkprop
1904
+
1905
+	/**
1906
+	 * helper for property element creation
1907
+	 *
1908
+	 * @param  string XML namespace (optional)
1909
+	 * @param  string property name
1910
+	 * @param  string property value
1911
+	 * @return array  property array
1912
+	 */
1913
+	function mkprop()
1914
+	{
1915
+		$args = func_get_args();
1916
+
1917
+		$prop = array();
1918
+		$prop['ns'] = 'DAV:';
1919
+		if (count($args) > 2) {
1920
+			$prop['ns'] = array_shift($args);
1921
+		}
1922
+
1923
+		$prop['name'] = array_shift($args);
1924
+		$prop['value'] = array_shift($args);
1925
+		$prop['status'] = array_shift($args);
1926
+
1927
+		return $prop;
1928
+	}
1929
+
1930
+	// }}}
1931
+
1932
+	// {{{ check_auth_wrapper
1933
+
1934
+	/**
1935
+	 * check authentication if implemented
1936
+	 *
1937
+	 * @param  void
1938
+	 * @return bool  true if authentication succeded or not necessary
1939
+	 */
1940
+	function check_auth_wrapper()
1941
+	{
1942
+		if (method_exists($this, 'checkAuth')) {
1943
+
1944
+			// PEAR style method name
1945
+			return $this->checkAuth(@$_SERVER['AUTH_TYPE'],
1946
+				@$_SERVER['PHP_AUTH_USER'],
1947
+				@$_SERVER['PHP_AUTH_PW']);
1948
+		}
1949
+
1950
+		if (method_exists($this, 'check_auth')) {
1951
+
1952
+			// old (pre 1.0) method name
1953
+			return $this->check_auth(@$_SERVER['AUTH_TYPE'],
1954
+				@$_SERVER['PHP_AUTH_USER'],
1955
+				@$_SERVER['PHP_AUTH_PW']);
1956
+		}
1957
+
1958
+		// no method found -> no authentication required
1959
+		return true;
1960
+	}
1961
+
1962
+	// }}}
1963
+
1964
+	// {{{ UUID stuff
1965
+
1966
+	/**
1967
+	 * generate Unique Universal IDentifier for lock token
1968
+	 *
1969
+	 * @param void
1970
+	 * @return string a new UUID
1971
+	 */
1972
+	function _new_uuid()
1973
+	{
1974
+		// use uuid extension from PECL if available
1975
+		if (function_exists('uuid_create')) {
1976
+			return uuid_create();
1977
+		}
1978
+
1979
+		// fallback
1980
+		$uuid = md5(microtime() . getmypid()); // this should be random enough for now
1981
+
1982
+		// set variant and version fields for 'true' random uuid
1983
+		$uuid{12} = '4';
1984
+		$n = 8 + (ord($uuid{16}) & 3);
1985
+		$hex = '0123456789abcdef';
1986
+		$uuid{16} = $hex{$n};
1987
+
1988
+		// return formated uuid
1989
+		return substr($uuid,  0, 8) . '-' .
1990
+			substr($uuid,  8, 4) . '-' .
1991
+			substr($uuid, 12, 4) . '-' .
1992
+			substr($uuid, 16, 4) . '-' .
1993
+			substr($uuid, 20);
1994
+	}
1995
+
1996
+	/**
1997
+	 * create a new opaque lock token as defined in RFC2518
1998
+	 *
1999
+	 * @param  void
2000
+	 * @return string new RFC2518 opaque lock token
2001
+	 */
2002
+	function _new_locktoken()
2003
+	{
2004
+		return 'opaquelocktoken:' . $this->_new_uuid();
2005
+	}
2006
+
2007
+	// }}}
2008
+
2009
+	// {{{ WebDAV If: header parsing
2010
+
2011
+	/**
2012
+	 *
2013
+	 *
2014
+	 * @param string header string to parse
2015
+	 * @param int current parsing position
2016
+	 * @return array next token (type and value)
2017
+	 */
2018
+	function _if_header_lexer($string, &$pos)
2019
+	{
2020
+		// skip whitespace
2021
+		while (ctype_space($string{$pos})) {
2022
+			++$pos;
2023
+		}
2024
+
2025
+		// already at end of string?
2026
+		if (strlen($string) <= $pos) {
2027
+			return;
2028
+		}
2029
+
2030
+		// get next character
2031
+		$c = $string{$pos++};
2032
+
2033
+		// now it depends on what we found
2034
+		switch ($c) {
2035
+
2036
+			// URIs are enclosed in <...>
2037
+			case '<':
2038
+				$pos2 = strpos($string, '>', $pos);
2039
+				$uri = substr($string, $pos, $pos2 - $pos);
2040
+				$pos = $pos2 + 1;
2041
+				return array('URI', $uri);
2042
+
2043
+			// ETags are enclosed in [...]
2044
+			case '[':
2045
+				$type = 'ETAG_STRONG';
2046
+				if ($string{$pos} == 'W') {
2047
+					$type = 'ETAG_WEAK';
2048
+					$pos += 2;
2049
+				}
2050
+
2051
+				$pos2 = strpos($string, ']', $pos);
2052
+				$etag = substr($string, $pos + 1, $pos2 - $pos - 2);
2053
+				$pos = $pos2 + 1;
2054
+				return array($type, $etag);
2055
+
2056
+			// 'N' indicates negation
2057
+			case 'N':
2058
+				$pos += 2;
2059
+				return array('NOT', 'Not');
2060
+
2061
+			// anything else is passed verbatim char by char
2062
+			default:
2063
+				return array('CHAR', $c);
2064
+		}
2065
+	}
2066
+
2067
+	/**
2068
+	 * parse If: header
2069
+	 *
2070
+	 * @param  string  header string
2071
+	 * @return array   URIs and their conditions
2072
+	 */
2073
+	function _if_header_parser($str)
2074
+	{
2075
+		$pos = 0;
2076
+		$len = strlen($str);
2077
+
2078
+		$uris = array();
2079
+
2080
+		// parser loop
2081
+		while ($pos < $len) {
2082
+
2083
+			// get next token
2084
+			$token = $this->_if_header_lexer($str, $pos);
2085
+
2086
+			// check for URI
2087
+			$uri = '';
2088
+			if ($token[0] == 'URI') {
2089
+				$uri = $token[1]; // remember URI
2090
+				$token = $this->_if_header_lexer($str, $pos); // get next token
2091
+			}
2092
+
2093
+			// sanity check
2094
+			if ($token[0] != 'CHAR' || $token[1] != '(') {
2095
+				return;
2096
+			}
2097
+
2098
+			$list = array();
2099
+			$level = 1;
2100
+			while ($level) {
2101
+				$token = $this->_if_header_lexer($str, $pos);
2102
+
2103
+				$not = '';
2104
+				if ($token[0] == 'NOT') {
2105
+					$not = '!';
2106
+					$token = $this->_if_header_lexer($str, $pos);
2107
+				}
2108
+
2109
+				switch ($token[0]) {
2110
+					case 'CHAR':
2111
+						switch ($token[1]) {
2112
+							case '(':
2113
+								$level++;
2114
+								break;
2115
+
2116
+							case ')':
2117
+								$level--;
2118
+								break;
2119
+
2120
+							default:
2121
+								return;
2122
+						}
2123
+						break;
2124
+
2125
+					case 'URI':
2126
+						$list[] = $not . "<$token[1]>";
2127
+						break;
2128
+
2129
+					case 'ETAG_WEAK':
2130
+						$list[] = $not . "[W/'$token[1]']>";
2131
+						break;
2132
+
2133
+					case 'ETAG_STRONG':
2134
+						$list[] = $not . "['$token[1]']>";
2135
+						break;
2136
+
2137
+					default:
2138
+						return;
2139
+				}
2140
+			}
2141
+
2142
+			if (is_array($uris[$uri])) {
2143
+				$uris[$uri] = array_merge($uris[$uri], $list);
2144
+				continue;
2145
+			}
2146
+			$uris[$uri] = $list;
2147
+		}
2148
+
2149
+		return $uris;
2150
+	}
2151
+
2152
+	/**
2153
+	 * check if conditions from If: headers are met
2154
+	 *
2155
+	 * the If: header is an extension to HTTP/1.1
2156
+	 * defined in RFC2518 9.4
2157
+	 *
2158
+	 * @param  void
2159
+	 * @return void
2160
+	 */
2161
+	function _check_if_header_conditions()
2162
+	{
2163
+		if (!isset($_SERVER['HTTP_IF'])) {
2164
+			return true;
2165
+		}
2166
+
2167
+		$this->_if_header_uris =
2168
+			$this->_if_header_parser($_SERVER['HTTP_IF']);
2169
+
2170
+		// any match is ok
2171
+		foreach($this->_if_header_uris as $uri => $conditions) {
2172
+			if (empty($uri)) {
2173
+				$uri = $this->base_uri . '/' . $this->path;
2174
+			}
2175
+
2176
+			// all must match
2177
+			foreach ($conditions as $condition) {
2178
+
2179
+				// lock tokens may be free form (RFC2518 6.3)
2180
+				// but if opaquelocktokens are used (RFC2518 6.4)
2181
+				// we have to check the format (litmus tests this)
2182
+				if (!strncmp($condition, '<opaquelocktoken:', strlen('<opaquelocktoken'))) {
2183
+					if (!ereg('^<opaquelocktoken:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}>$', $condition)) {
2184
+						return;
2185
+					}
2186
+				}
2187
+
2188
+				if (!$this->_check_uri_condition($uri, $condition)) {
2189
+					continue (2);
2190
+				}
2191
+			}
2192
+
2193
+			return true;
2194
+		}
2195
+	}
2196
+
2197
+	/**
2198
+	 * Check a single URI condition parsed from an if-header
2199
+	 *
2200
+	 * @abstract
2201
+	 * @param string $uri URI to check
2202
+	 * @param string $condition Condition to check for this URI
2203
+	 * @returns bool Condition check result
2204
+	 */
2205
+	function _check_uri_condition($uri, $condition)
2206
+	{
2207
+		// not really implemented here,
2208
+		// implementations must override
2209
+		return true;
2210
+	}
2211
+
2212
+	/**
2213
+	 * @param array of locks
2214
+	 * @param bool exclusive lock?
2215
+	 */
2216
+	function check_lock_helper($lock, $exclusive_only = false)
2217
+	{
2218
+		if (!is_array($lock) || empty($lock)) {
2219
+			return true;
2220
+		}
2221
+
2222
+		// FIXME doesn't check uri restrictions yet
2223
+		if (strstr($_SERVER['HTTP_IF'], $lock['token'])) {
2224
+			return true;
2225
+		}
2226
+
2227
+		if ($exclusive_only && ($lock['scope'] == 'shared')) {
2228
+			return true;
2229
+		}
2230
+	}
2231
+
2232
+	/**
2233
+	 * @param string path of resource to check
2234
+	 * @param bool exclusive lock?
2235
+	 */
2236
+	function check_lock_wrapper($path, $exclusive_only = false)
2237
+	{
2238
+		if (!method_exists($this, 'getLocks')) {
2239
+			return true;
2240
+		}
2241
+
2242
+		$lock = $this->getLocks($path);
2243
+
2244
+		return $this->check_lock_helper($lock, $exclusive_only);
2245
+	}
2246
+
2247
+	// }}}
2248
+
2249
+	function _lockentries($locks)
2250
+	{
2251
+		if (!is_array($locks) || empty($locks)) {
2252
+			return '';
2253
+		}
2254
+
2255
+		foreach ($locks as $key => $lock) {
2256
+			if (!is_array($lock) || empty($lock)) {
2257
+				continue;
2258
+			}
2259
+
2260
+			$locks[$key] = "<D:lockentry>
2261 2261
        <D:lockscope><D:$lock[scope]/></D:lockscope>
2262 2262
        <D:locktype><D:$lock[type]/></D:locktype>
2263 2263
       </D:lockentry>";
2264
-        }
2265
-
2266
-        return implode('', $locks);
2267
-    }
2268
-
2269
-    function _activelocks($locks)
2270
-    {
2271
-        if (!is_array($locks) || empty($locks)) {
2272
-            return '';
2273
-        }
2274
-
2275
-        foreach ($locks as $key => $lock) {
2276
-            if (!is_array($lock) || empty($lock)) {
2277
-                continue;
2278
-            }
2279
-
2280
-            // check for 'timeout' or 'expires'
2281
-            $timeout = 'Infinite';
2282
-            if (!empty($lock['expires'])) {
2283
-                $timeout = 'Second-' . ($lock['expires'] - time());
2284
-            } else if (!empty($lock['timeout'])) {
2285
-
2286
-                // more than a million is considered an absolute timestamp
2287
-                // less is more likely a relative value
2288
-                $timeout = "Second-$lock[timeout]";
2289
-                if ($lock['timeout'] > 1000000) {
2290
-                    $timeout = 'Second-' . ($lock['timeout'] - time());
2291
-                }
2292
-            }
2293
-
2294
-            // genreate response block
2295
-                $locks[$key] = "<D:activelock>
2264
+		}
2265
+
2266
+		return implode('', $locks);
2267
+	}
2268
+
2269
+	function _activelocks($locks)
2270
+	{
2271
+		if (!is_array($locks) || empty($locks)) {
2272
+			return '';
2273
+		}
2274
+
2275
+		foreach ($locks as $key => $lock) {
2276
+			if (!is_array($lock) || empty($lock)) {
2277
+				continue;
2278
+			}
2279
+
2280
+			// check for 'timeout' or 'expires'
2281
+			$timeout = 'Infinite';
2282
+			if (!empty($lock['expires'])) {
2283
+				$timeout = 'Second-' . ($lock['expires'] - time());
2284
+			} else if (!empty($lock['timeout'])) {
2285
+
2286
+				// more than a million is considered an absolute timestamp
2287
+				// less is more likely a relative value
2288
+				$timeout = "Second-$lock[timeout]";
2289
+				if ($lock['timeout'] > 1000000) {
2290
+					$timeout = 'Second-' . ($lock['timeout'] - time());
2291
+				}
2292
+			}
2293
+
2294
+			// genreate response block
2295
+				$locks[$key] = "<D:activelock>
2296 2296
        <D:lockscope><D:$lock[scope]/></D:lockscope>
2297 2297
        <D:locktype><D:$lock[type]/></D:locktype>
2298 2298
        <D:depth>$lock[depth]</D:depth>
@@ -2300,102 +2300,102 @@  discard block
 block discarded – undo
2300 2300
        <D:timeout>$timeout</D:timeout>
2301 2301
        <D:locktoken><D:href>$lock[token]</D:href></D:locktoken>
2302 2302
       </D:activelock>";
2303
-        }
2304
-
2305
-        return implode('', $locks);
2306
-    }
2307
-
2308
-    /**
2309
-     * set HTTP return status and mirror it in a private header
2310
-     *
2311
-     * @param string status code and message
2312
-     * @return void
2313
-     */
2314
-    function http_status($status)
2315
-    {
2316
-        // simplified success case
2317
-        if ($status === true) {
2318
-            $status = '200 OK';
2319
-        }
2320
-
2321
-        // didn't set a more specific status code
2322
-        if (empty($status)) {
2323
-            $status = '500 Internal Server Error';
2324
-        }
2325
-
2326
-        // remember status
2327
-        $this->_http_status = $status;
2328
-
2329
-        // generate HTTP status response
2330
-        header("HTTP/1.1 $status");
2331
-        header("X-WebDAV-Status: $status", true);
2332
-    }
2333
-
2334
-    /**
2335
-     * private minimalistic version of PHP urlencode
2336
-     *
2337
-     * only blanks and XML special chars must be encoded here
2338
-     * full urlencode encoding confuses some clients...
2339
-     *
2340
-     * @param  string  URL to encode
2341
-     * @return string  encoded URL
2342
-     */
2343
-    function _urlencode($url)
2344
-    {
2345
-        return strtr($url, array(
2346
-                ' ' => '%20',
2347
-                '&' => '%26',
2348
-                '<' => '%3C',
2349
-                '>' => '%3E',
2350
-            ));
2351
-    }
2352
-
2353
-    /**
2354
-     * private version of PHP urldecode
2355
-     *
2356
-     * not really needed but added for completenes
2357
-     *
2358
-     * @param  string  URL to decode
2359
-     * @return string  decoded URL
2360
-     */
2361
-    function _urldecode($path)
2362
-    {
2363
-        return urldecode($path);
2364
-    }
2365
-
2366
-    /**
2367
-     * UTF-8 encode property values if not already done so
2368
-     *
2369
-     * @param  string  text to encode
2370
-     * @return string  utf-8 encoded text
2371
-     */
2372
-    function _prop_encode($text)
2373
-    {
2374
-        switch (strtolower($this->_prop_encoding)) {
2375
-        case 'utf-8':
2376
-            return $text;
2377
-        case 'iso-8859-1':
2378
-        case 'iso-8859-15':
2379
-        case 'latin-1':
2380
-        default:
2381
-            return utf8_encode($text);
2382
-        }
2383
-    }
2384
-
2385
-    /**
2386
-     * slashify - make sure path ends in a slash
2387
-     *
2388
-     * @param  string directory path
2389
-     * @return string directory path with trailing slash
2390
-     */
2391
-    function _slashify($path)
2392
-    {
2393
-        if (substr($path, -1) != '/') {
2394
-            $path .= '/';
2395
-        }
2396
-
2397
-        return $path;
2398
-    }
2303
+		}
2304
+
2305
+		return implode('', $locks);
2306
+	}
2307
+
2308
+	/**
2309
+	 * set HTTP return status and mirror it in a private header
2310
+	 *
2311
+	 * @param string status code and message
2312
+	 * @return void
2313
+	 */
2314
+	function http_status($status)
2315
+	{
2316
+		// simplified success case
2317
+		if ($status === true) {
2318
+			$status = '200 OK';
2319
+		}
2320
+
2321
+		// didn't set a more specific status code
2322
+		if (empty($status)) {
2323
+			$status = '500 Internal Server Error';
2324
+		}
2325
+
2326
+		// remember status
2327
+		$this->_http_status = $status;
2328
+
2329
+		// generate HTTP status response
2330
+		header("HTTP/1.1 $status");
2331
+		header("X-WebDAV-Status: $status", true);
2332
+	}
2333
+
2334
+	/**
2335
+	 * private minimalistic version of PHP urlencode
2336
+	 *
2337
+	 * only blanks and XML special chars must be encoded here
2338
+	 * full urlencode encoding confuses some clients...
2339
+	 *
2340
+	 * @param  string  URL to encode
2341
+	 * @return string  encoded URL
2342
+	 */
2343
+	function _urlencode($url)
2344
+	{
2345
+		return strtr($url, array(
2346
+				' ' => '%20',
2347
+				'&' => '%26',
2348
+				'<' => '%3C',
2349
+				'>' => '%3E',
2350
+			));
2351
+	}
2352
+
2353
+	/**
2354
+	 * private version of PHP urldecode
2355
+	 *
2356
+	 * not really needed but added for completenes
2357
+	 *
2358
+	 * @param  string  URL to decode
2359
+	 * @return string  decoded URL
2360
+	 */
2361
+	function _urldecode($path)
2362
+	{
2363
+		return urldecode($path);
2364
+	}
2365
+
2366
+	/**
2367
+	 * UTF-8 encode property values if not already done so
2368
+	 *
2369
+	 * @param  string  text to encode
2370
+	 * @return string  utf-8 encoded text
2371
+	 */
2372
+	function _prop_encode($text)
2373
+	{
2374
+		switch (strtolower($this->_prop_encoding)) {
2375
+		case 'utf-8':
2376
+			return $text;
2377
+		case 'iso-8859-1':
2378
+		case 'iso-8859-15':
2379
+		case 'latin-1':
2380
+		default:
2381
+			return utf8_encode($text);
2382
+		}
2383
+	}
2384
+
2385
+	/**
2386
+	 * slashify - make sure path ends in a slash
2387
+	 *
2388
+	 * @param  string directory path
2389
+	 * @return string directory path with trailing slash
2390
+	 */
2391
+	function _slashify($path)
2392
+	{
2393
+		if (substr($path, -1) != '/') {
2394
+			$path .= '/';
2395
+		}
2396
+
2397
+		return $path;
2398
+	}
2399 2399
 }
2400 2400
 
2401 2401
 // Local variables:
Please login to merge, or discard this patch.
phpicalendar/lib/HTTP/WebDAV/Tools/_parse_propfind.php 1 patch
Indentation   +136 added lines, -136 removed lines patch added patch discarded remove patch
@@ -27,141 +27,141 @@
 block discarded – undo
27 27
  */
28 28
 class _parse_propfind
29 29
 {
30
-    /**
31
-     * success state flag
32
-     *
33
-     * @var bool
34
-     * @access public
35
-     */
36
-    var $success = false;
37
-
38
-    /**
39
-     * found properties are collected here
40
-     *
41
-     * @var array
42
-     * @access public
43
-     */
44
-    var $props = false;
45
-
46
-    /**
47
-     * internal tag nesting depth counter
48
-     *
49
-     * @var int
50
-     * @access private
51
-     */
52
-    var $depth = 0;
53
-
54
-    /**
55
-     * constructor
56
-     *
57
-     * @access public
58
-     */
59
-    function _parse_propfind($input)
60
-    {
61
-        // success state flag
62
-        $this->success = true;
63
-
64
-        // property storage array
65
-        $this->props = array();
66
-
67
-        // internal tag depth counter
68
-        $this->depth = 0;
69
-
70
-        // remember if any input was parsed
71
-        $had_input = false;
72
-
73
-        // open input stream
74
-        $handle = fopen($input, 'r');
75
-        if (!$handle) {
76
-            $this->success = false;
77
-            return;
78
-        }
79
-
80
-        // create XML parser
81
-        $parser = xml_parser_create_ns('UTF-8', ' ');
82
-
83
-        // set tag and data handlers
84
-        xml_set_element_handler($parser, array(&$this, '_startElement'),
85
-            array(&$this, '_endElement'));
86
-
87
-        // we want a case sensitive parser
88
-        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
89
-
90
-        // parse input
91
-        while ($this->success && !feof($handle)) {
92
-            $line = fgets($handle);
93
-            if (is_string($line)) {
94
-                $had_input = true;
95
-                $this->success &= xml_parse($parser, $line, false);
96
-            }
97
-        }
98
-
99
-        // finish parsing
100
-        if ($had_input) {
101
-            $this->success &= xml_parse($parser, '', true);
102
-        }
103
-
104
-        // free parser
105
-        xml_parser_free($parser);
106
-
107
-        // close input stream
108
-        fclose($handle);
109
-
110
-        // if no input was parsed it was a request
111
-        if (empty($this->props)) {
112
-            $this->props = 'allprop';
113
-        }
114
-    }
115
-
116
-    /**
117
-     * start tag handler
118
-     *
119
-     * @access private
120
-     * @param resource parser
121
-     * @param string tag name
122
-     * @param array tag attributes
123
-     */
124
-    function _startElement($parser, $name, $attrs)
125
-    {
126
-        // name space handling
127
-        if (strstr($name, ' ')) {
128
-            list ($ns, $name) = explode(' ', $name);
129
-            if (!$ns) {
130
-                $this->success = false;
131
-            }
132
-        }
133
-
134
-        // special tags at level 1: <allprop> and <propname>
135
-        if ($this->depth == 1) {
136
-            if ($name == 'allprop' || $name == 'propname') {
137
-                $this->props = $name;
138
-            }
139
-        }
140
-
141
-        // requested properties are found at level 2
142
-        if ($this->depth == 2) {
143
-            $prop = array('name' => $name);
144
-            if ($ns) {
145
-                $prop['ns'] = $ns;
146
-            }
147
-            $this->props[] = $prop;
148
-        }
149
-
150
-        // increment depth count
151
-        $this->depth++;
152
-    }
153
-
154
-    /**
155
-     * end tag handler
156
-     *
157
-     * @access private
158
-     * @param resource parser
159
-     * @param string tag name
160
-     */
161
-    function _endElement($parser, $name)
162
-    {
163
-        // here we only need to decrement the depth count
164
-        $this->depth--;
165
-    }
30
+	/**
31
+	 * success state flag
32
+	 *
33
+	 * @var bool
34
+	 * @access public
35
+	 */
36
+	var $success = false;
37
+
38
+	/**
39
+	 * found properties are collected here
40
+	 *
41
+	 * @var array
42
+	 * @access public
43
+	 */
44
+	var $props = false;
45
+
46
+	/**
47
+	 * internal tag nesting depth counter
48
+	 *
49
+	 * @var int
50
+	 * @access private
51
+	 */
52
+	var $depth = 0;
53
+
54
+	/**
55
+	 * constructor
56
+	 *
57
+	 * @access public
58
+	 */
59
+	function _parse_propfind($input)
60
+	{
61
+		// success state flag
62
+		$this->success = true;
63
+
64
+		// property storage array
65
+		$this->props = array();
66
+
67
+		// internal tag depth counter
68
+		$this->depth = 0;
69
+
70
+		// remember if any input was parsed
71
+		$had_input = false;
72
+
73
+		// open input stream
74
+		$handle = fopen($input, 'r');
75
+		if (!$handle) {
76
+			$this->success = false;
77
+			return;
78
+		}
79
+
80
+		// create XML parser
81
+		$parser = xml_parser_create_ns('UTF-8', ' ');
82
+
83
+		// set tag and data handlers
84
+		xml_set_element_handler($parser, array(&$this, '_startElement'),
85
+			array(&$this, '_endElement'));
86
+
87
+		// we want a case sensitive parser
88
+		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
89
+
90
+		// parse input
91
+		while ($this->success && !feof($handle)) {
92
+			$line = fgets($handle);
93
+			if (is_string($line)) {
94
+				$had_input = true;
95
+				$this->success &= xml_parse($parser, $line, false);
96
+			}
97
+		}
98
+
99
+		// finish parsing
100
+		if ($had_input) {
101
+			$this->success &= xml_parse($parser, '', true);
102
+		}
103
+
104
+		// free parser
105
+		xml_parser_free($parser);
106
+
107
+		// close input stream
108
+		fclose($handle);
109
+
110
+		// if no input was parsed it was a request
111
+		if (empty($this->props)) {
112
+			$this->props = 'allprop';
113
+		}
114
+	}
115
+
116
+	/**
117
+	 * start tag handler
118
+	 *
119
+	 * @access private
120
+	 * @param resource parser
121
+	 * @param string tag name
122
+	 * @param array tag attributes
123
+	 */
124
+	function _startElement($parser, $name, $attrs)
125
+	{
126
+		// name space handling
127
+		if (strstr($name, ' ')) {
128
+			list ($ns, $name) = explode(' ', $name);
129
+			if (!$ns) {
130
+				$this->success = false;
131
+			}
132
+		}
133
+
134
+		// special tags at level 1: <allprop> and <propname>
135
+		if ($this->depth == 1) {
136
+			if ($name == 'allprop' || $name == 'propname') {
137
+				$this->props = $name;
138
+			}
139
+		}
140
+
141
+		// requested properties are found at level 2
142
+		if ($this->depth == 2) {
143
+			$prop = array('name' => $name);
144
+			if ($ns) {
145
+				$prop['ns'] = $ns;
146
+			}
147
+			$this->props[] = $prop;
148
+		}
149
+
150
+		// increment depth count
151
+		$this->depth++;
152
+	}
153
+
154
+	/**
155
+	 * end tag handler
156
+	 *
157
+	 * @access private
158
+	 * @param resource parser
159
+	 * @param string tag name
160
+	 */
161
+	function _endElement($parser, $name)
162
+	{
163
+		// here we only need to decrement the depth count
164
+		$this->depth--;
165
+	}
166 166
 }
167 167
 ?>
Please login to merge, or discard this patch.
phpicalendar/lib/HTTP/WebDAV/Tools/_parse_proppatch.php 1 patch
Indentation   +176 added lines, -176 removed lines patch added patch discarded remove patch
@@ -29,186 +29,186 @@
 block discarded – undo
29 29
  */
30 30
 class _parse_proppatch 
31 31
 {
32
-    /**
33
-     *
34
-     * 
35
-     * @var
36
-     * @access
37
-     */
38
-    var $success;
39
-
40
-    /**
41
-     *
42
-     * 
43
-     * @var
44
-     * @access
45
-     */
46
-    var $props;
47
-
48
-    /**
49
-     *
50
-     * 
51
-     * @var
52
-     * @access
53
-     */
54
-    var $depth;
55
-
56
-    /**
57
-     *
58
-     * 
59
-     * @var
60
-     * @access
61
-     */
62
-    var $mode;
63
-
64
-    /**
65
-     *
66
-     * 
67
-     * @var
68
-     * @access
69
-     */
70
-    var $current;
71
-
72
-    /**
73
-     * constructor
74
-     * 
75
-     * @param  string  path of input stream 
76
-     * @access public
77
-     */
78
-    function _parse_proppatch($path) 
79
-    {
80
-        $this->success = true;
81
-
82
-        $this->depth = 0;
83
-        $this->props = array();
84
-        $had_input = false;
85
-
86
-        $f_in = fopen($path, "r");
87
-        if (!$f_in) {
88
-            $this->success = false;
89
-            return;
90
-        }
91
-
92
-        $xml_parser = xml_parser_create_ns("UTF-8", " ");
93
-
94
-        xml_set_element_handler($xml_parser,
95
-                                array(&$this, "_startElement"),
96
-                                array(&$this, "_endElement"));
97
-
98
-        xml_set_character_data_handler($xml_parser,
99
-                                       array(&$this, "_data"));
100
-
101
-        xml_parser_set_option($xml_parser,
102
-                              XML_OPTION_CASE_FOLDING, false);
103
-
104
-        while($this->success && !feof($f_in)) {
105
-            $line = fgets($f_in);
106
-            if (is_string($line)) {
107
-                $had_input = true;
108
-                $this->success &= xml_parse($xml_parser, $line, false);
109
-            }
110
-        } 
32
+	/**
33
+	 *
34
+	 * 
35
+	 * @var
36
+	 * @access
37
+	 */
38
+	var $success;
39
+
40
+	/**
41
+	 *
42
+	 * 
43
+	 * @var
44
+	 * @access
45
+	 */
46
+	var $props;
47
+
48
+	/**
49
+	 *
50
+	 * 
51
+	 * @var
52
+	 * @access
53
+	 */
54
+	var $depth;
55
+
56
+	/**
57
+	 *
58
+	 * 
59
+	 * @var
60
+	 * @access
61
+	 */
62
+	var $mode;
63
+
64
+	/**
65
+	 *
66
+	 * 
67
+	 * @var
68
+	 * @access
69
+	 */
70
+	var $current;
71
+
72
+	/**
73
+	 * constructor
74
+	 * 
75
+	 * @param  string  path of input stream 
76
+	 * @access public
77
+	 */
78
+	function _parse_proppatch($path) 
79
+	{
80
+		$this->success = true;
81
+
82
+		$this->depth = 0;
83
+		$this->props = array();
84
+		$had_input = false;
85
+
86
+		$f_in = fopen($path, "r");
87
+		if (!$f_in) {
88
+			$this->success = false;
89
+			return;
90
+		}
91
+
92
+		$xml_parser = xml_parser_create_ns("UTF-8", " ");
93
+
94
+		xml_set_element_handler($xml_parser,
95
+								array(&$this, "_startElement"),
96
+								array(&$this, "_endElement"));
97
+
98
+		xml_set_character_data_handler($xml_parser,
99
+									   array(&$this, "_data"));
100
+
101
+		xml_parser_set_option($xml_parser,
102
+							  XML_OPTION_CASE_FOLDING, false);
103
+
104
+		while($this->success && !feof($f_in)) {
105
+			$line = fgets($f_in);
106
+			if (is_string($line)) {
107
+				$had_input = true;
108
+				$this->success &= xml_parse($xml_parser, $line, false);
109
+			}
110
+		} 
111 111
         
112
-        if($had_input) {
113
-            $this->success &= xml_parse($xml_parser, "", true);
114
-        }
115
-
116
-        xml_parser_free($xml_parser);
117
-
118
-        fclose($f_in);
119
-    }
120
-
121
-    /**
122
-     * tag start handler
123
-     *
124
-     * @param  resource  parser
125
-     * @param  string    tag name
126
-     * @param  array     tag attributes
127
-     * @return void
128
-     * @access private
129
-     */
130
-    function _startElement($parser, $name, $attrs) 
131
-    {
132
-        if (strstr($name, " ")) {
133
-            list($ns, $tag) = explode(" ", $name);
134
-            if ($ns == "")
135
-                $this->success = false;
136
-        } else {
137
-            $ns = "";
138
-            $tag = $name;
139
-        }
140
-
141
-        if ($this->depth == 1) {
142
-            $this->mode = $tag;
143
-        } 
144
-
145
-        if ($this->depth == 3) {
146
-            $prop = array("name" => $tag);
147
-            $this->current = array("name" => $tag, "ns" => $ns, "status"=> 200);
148
-            if ($this->mode == "set") {
149
-                $this->current["val"] = "";     // default set val
150
-            }
151
-        }
152
-
153
-        if ($this->depth >= 4) {
154
-            $this->current["val"] .= "<$tag";
155
-            foreach ($attr as $key => $val) {
156
-                $this->current["val"] .= ' '.$key.'="'.str_replace('"','&quot;', $val).'"';
157
-            }
158
-            $this->current["val"] .= ">";
159
-        }
112
+		if($had_input) {
113
+			$this->success &= xml_parse($xml_parser, "", true);
114
+		}
115
+
116
+		xml_parser_free($xml_parser);
117
+
118
+		fclose($f_in);
119
+	}
120
+
121
+	/**
122
+	 * tag start handler
123
+	 *
124
+	 * @param  resource  parser
125
+	 * @param  string    tag name
126
+	 * @param  array     tag attributes
127
+	 * @return void
128
+	 * @access private
129
+	 */
130
+	function _startElement($parser, $name, $attrs) 
131
+	{
132
+		if (strstr($name, " ")) {
133
+			list($ns, $tag) = explode(" ", $name);
134
+			if ($ns == "")
135
+				$this->success = false;
136
+		} else {
137
+			$ns = "";
138
+			$tag = $name;
139
+		}
140
+
141
+		if ($this->depth == 1) {
142
+			$this->mode = $tag;
143
+		} 
144
+
145
+		if ($this->depth == 3) {
146
+			$prop = array("name" => $tag);
147
+			$this->current = array("name" => $tag, "ns" => $ns, "status"=> 200);
148
+			if ($this->mode == "set") {
149
+				$this->current["val"] = "";     // default set val
150
+			}
151
+		}
152
+
153
+		if ($this->depth >= 4) {
154
+			$this->current["val"] .= "<$tag";
155
+			foreach ($attr as $key => $val) {
156
+				$this->current["val"] .= ' '.$key.'="'.str_replace('"','&quot;', $val).'"';
157
+			}
158
+			$this->current["val"] .= ">";
159
+		}
160 160
 
161 161
         
162 162
 
163
-        $this->depth++;
164
-    }
165
-
166
-    /**
167
-     * tag end handler
168
-     *
169
-     * @param  resource  parser
170
-     * @param  string    tag name
171
-     * @return void
172
-     * @access private
173
-     */
174
-    function _endElement($parser, $name) 
175
-    {
176
-        if (strstr($name, " ")) {
177
-            list($ns, $tag) = explode(" ", $name);
178
-            if ($ns == "")
179
-                $this->success = false;
180
-        } else {
181
-            $ns = "";
182
-            $tag = $name;
183
-        }
184
-
185
-        $this->depth--;
186
-
187
-        if ($this->depth >= 4) {
188
-            $this->current["val"] .= "</$tag>";
189
-        }
190
-
191
-        if ($this->depth == 3) {
192
-            if (isset($this->current)) {
193
-                $this->props[] = $this->current;
194
-                unset($this->current);
195
-            }
196
-        }
197
-    }
198
-
199
-    /**
200
-     * input data handler
201
-     *
202
-     * @param  resource  parser
203
-     * @param  string    data
204
-     * @return void
205
-     * @access private
206
-     */
207
-    function _data($parser, $data) {
208
-        if (isset($this->current)) {
209
-            $this->current["val"] .= $data;
210
-        }
211
-    }
163
+		$this->depth++;
164
+	}
165
+
166
+	/**
167
+	 * tag end handler
168
+	 *
169
+	 * @param  resource  parser
170
+	 * @param  string    tag name
171
+	 * @return void
172
+	 * @access private
173
+	 */
174
+	function _endElement($parser, $name) 
175
+	{
176
+		if (strstr($name, " ")) {
177
+			list($ns, $tag) = explode(" ", $name);
178
+			if ($ns == "")
179
+				$this->success = false;
180
+		} else {
181
+			$ns = "";
182
+			$tag = $name;
183
+		}
184
+
185
+		$this->depth--;
186
+
187
+		if ($this->depth >= 4) {
188
+			$this->current["val"] .= "</$tag>";
189
+		}
190
+
191
+		if ($this->depth == 3) {
192
+			if (isset($this->current)) {
193
+				$this->props[] = $this->current;
194
+				unset($this->current);
195
+			}
196
+		}
197
+	}
198
+
199
+	/**
200
+	 * input data handler
201
+	 *
202
+	 * @param  resource  parser
203
+	 * @param  string    data
204
+	 * @return void
205
+	 * @access private
206
+	 */
207
+	function _data($parser, $data) {
208
+		if (isset($this->current)) {
209
+			$this->current["val"] .= $data;
210
+		}
211
+	}
212 212
 }
213 213
 
214 214
 ?>
215 215
\ No newline at end of file
Please login to merge, or discard this patch.
phpicalendar/lib/HTTP/WebDAV/Tools/_parse_lockinfo.php 1 patch
Indentation   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
 	 * @param  string  path of stream to read
76 76
 	 * @access public
77 77
 	 */
78
-    function _parse_lockinfo($path) 
78
+	function _parse_lockinfo($path) 
79 79
 	{
80 80
 		// we assume success unless problems occur
81 81
 		$this->success = true;
@@ -139,32 +139,32 @@  discard block
 block discarded – undo
139 139
 	 * @return void
140 140
 	 * @access private
141 141
 	 */
142
-    function _startElement($parser, $name, $attrs) 
143
-    {
142
+	function _startElement($parser, $name, $attrs) 
143
+	{
144 144
 		// namespace handling
145
-        if (strstr($name, " ")) {
146
-            list($ns, $tag) = explode(" ", $name);
147
-        } else {
148
-            $ns = "";
149
-            $tag = $name;
150
-        }
145
+		if (strstr($name, " ")) {
146
+			list($ns, $tag) = explode(" ", $name);
147
+		} else {
148
+			$ns = "";
149
+			$tag = $name;
150
+		}
151 151
 		
152 152
   
153
-        if ($this->collect_owner) {
153
+		if ($this->collect_owner) {
154 154
 			// everything within the <owner> tag needs to be collected
155
-            $ns_short = "";
156
-            $ns_attr = "";
157
-            if ($ns) {
158
-                if ($ns == "DAV:") {
159
-                    $ns_short = "D:";
160
-                } else {
161
-                    $ns_attr = " xmlns='$ns'";
162
-                }
163
-            }
164
-            $this->owner .= "<$ns_short$tag$ns_attr>";
165
-        } else if ($ns == "DAV:") {
155
+			$ns_short = "";
156
+			$ns_attr = "";
157
+			if ($ns) {
158
+				if ($ns == "DAV:") {
159
+					$ns_short = "D:";
160
+				} else {
161
+					$ns_attr = " xmlns='$ns'";
162
+				}
163
+			}
164
+			$this->owner .= "<$ns_short$tag$ns_attr>";
165
+		} else if ($ns == "DAV:") {
166 166
 			// parse only the essential tags
167
-            switch ($tag) {
167
+			switch ($tag) {
168 168
 			case "write":
169 169
 				$this->locktype = $tag;
170 170
 				break;
@@ -175,9 +175,9 @@  discard block
 block discarded – undo
175 175
 			case "owner":
176 176
 				$this->collect_owner = true;
177 177
 				break;
178
-            }
179
-        }
180
-    }
178
+			}
179
+		}
180
+	}
181 181
 	
182 182
 	/**
183 183
 	 * data handler
@@ -187,13 +187,13 @@  discard block
 block discarded – undo
187 187
 	 * @return void
188 188
 	 * @access private
189 189
 	 */
190
-    function _data($parser, $data) 
191
-    {
190
+	function _data($parser, $data) 
191
+	{
192 192
 		// only the <owner> tag has data content
193
-        if ($this->collect_owner) {
194
-            $this->owner .= $data;
195
-        }
196
-    }
193
+		if ($this->collect_owner) {
194
+			$this->owner .= $data;
195
+		}
196
+	}
197 197
 
198 198
 	/**
199 199
 	 * tag end handler
@@ -203,8 +203,8 @@  discard block
 block discarded – undo
203 203
 	 * @return void
204 204
 	 * @access private
205 205
 	 */
206
-    function _endElement($parser, $name) 
207
-    {
206
+	function _endElement($parser, $name) 
207
+	{
208 208
 		// namespace handling
209 209
 		if (strstr($name, " ")) {
210 210
 			list($ns, $tag) = explode(" ", $name);
@@ -224,14 +224,14 @@  discard block
 block discarded – undo
224 224
 			$ns_attr = "";
225 225
 			if ($ns) {
226 226
 				if ($ns == "DAV:") {
227
-                    $ns_short = "D:";
227
+					$ns_short = "D:";
228 228
 				} else {
229 229
 					$ns_attr = " xmlns='$ns'";
230 230
 				}
231 231
 			}
232 232
 			$this->owner .= "</$ns_short$tag$ns_attr>";
233 233
 		}
234
-    }
234
+	}
235 235
 }
236 236
 
237 237
 ?>
238 238
\ No newline at end of file
Please login to merge, or discard this patch.
phpicalendar/lib/bennu/bennu.class.php 1 patch
Indentation   +30 added lines, -30 removed lines patch added patch discarded remove patch
@@ -14,46 +14,46 @@
 block discarded – undo
14 14
  */
15 15
 
16 16
 class Bennu {
17
-    function timestamp_to_datetime($t = NULL) {
18
-        if($t === NULL) {
19
-            $t = time();
20
-        }
21
-        return gmstrftime('%Y%m%dT%H%M%SZ', $t);
22
-    }
17
+	function timestamp_to_datetime($t = NULL) {
18
+		if($t === NULL) {
19
+			$t = time();
20
+		}
21
+		return gmstrftime('%Y%m%dT%H%M%SZ', $t);
22
+	}
23 23
 
24
-    function generate_guid() {
25
-        // Implemented as per the Network Working Group draft on UUIDs and GUIDs
24
+	function generate_guid() {
25
+		// Implemented as per the Network Working Group draft on UUIDs and GUIDs
26 26
     
27
-        // These two octets get special treatment
28
-        $time_hi_and_version       = sprintf('%02x', (1 << 6) + mt_rand(0, 15)); // 0100 plus 4 random bits
29
-        $clock_seq_hi_and_reserved = sprintf('%02x', (1 << 7) + mt_rand(0, 63)); // 10 plus 6 random bits
27
+		// These two octets get special treatment
28
+		$time_hi_and_version       = sprintf('%02x', (1 << 6) + mt_rand(0, 15)); // 0100 plus 4 random bits
29
+		$clock_seq_hi_and_reserved = sprintf('%02x', (1 << 7) + mt_rand(0, 63)); // 10 plus 6 random bits
30 30
     
31
-        // Need another 14 random octects
32
-        $pool = '';
33
-        for($i = 0; $i < 7; ++$i) {
34
-            $pool .= sprintf('%04x', mt_rand(0, 65535));
35
-        }
31
+		// Need another 14 random octects
32
+		$pool = '';
33
+		for($i = 0; $i < 7; ++$i) {
34
+			$pool .= sprintf('%04x', mt_rand(0, 65535));
35
+		}
36 36
     
37
-        // time_low = 4 octets
38
-        $random  = substr($pool, 0, 8).'-';
37
+		// time_low = 4 octets
38
+		$random  = substr($pool, 0, 8).'-';
39 39
     
40
-        // time_mid = 2 octets
41
-        $random .= substr($pool, 8, 4).'-';
40
+		// time_mid = 2 octets
41
+		$random .= substr($pool, 8, 4).'-';
42 42
     
43
-        // time_high_and_version = 2 octets
44
-        $random .= $time_hi_and_version.substr($pool, 12, 2).'-';
43
+		// time_high_and_version = 2 octets
44
+		$random .= $time_hi_and_version.substr($pool, 12, 2).'-';
45 45
     
46
-        // clock_seq_high_and_reserved = 1 octet
47
-        $random .= $clock_seq_hi_and_reserved;
46
+		// clock_seq_high_and_reserved = 1 octet
47
+		$random .= $clock_seq_hi_and_reserved;
48 48
     
49
-        // clock_seq_low = 1 octet
50
-        $random .= substr($pool, 13, 2).'-';
49
+		// clock_seq_low = 1 octet
50
+		$random .= substr($pool, 13, 2).'-';
51 51
     
52
-        // node = 6 octets
53
-        $random .= substr($pool, 14, 12);
52
+		// node = 6 octets
53
+		$random .= substr($pool, 14, 12);
54 54
     
55
-        return $random;
56
-    }
55
+		return $random;
56
+	}
57 57
 }
58 58
 
59 59
 ?>
Please login to merge, or discard this patch.
phpicalendar/lib/bennu/iCalendar_components.php 1 patch
Indentation   +342 added lines, -342 removed lines patch added patch discarded remove patch
@@ -14,377 +14,377 @@
 block discarded – undo
14 14
  */
15 15
 
16 16
 class iCalendar_component {
17
-    var $name             = NULL;
18
-    var $properties       = NULL;
19
-    var $components       = NULL;
20
-    var $valid_properties = NULL;
21
-    var $valid_components = NULL;
22
-
23
-    function iCalendar_component() {
24
-        $this->construct();
25
-    }
26
-
27
-    function construct() {
28
-        // Initialize the components array
29
-        if(empty($this->components)) {
30
-            $this->components = array();
31
-            foreach($this->valid_components as $name) {
32
-                $this->components[$name] = array();
33
-            }
34
-        }
35
-    }
36
-
37
-    function get_name() {
38
-        return $this->name;
39
-    }
40
-
41
-    function add_property($name, $value = NULL, $parameters = NULL) {
42
-
43
-        // Uppercase first of all
44
-        $name = strtoupper($name);
45
-
46
-        // Are we trying to add a valid property?
47
-        $xname = false;
48
-        if(!isset($this->valid_properties[$name])) {
49
-            // If not, is it an x-name as per RFC 2445?
50
-            if(!rfc2445_is_xname($name)) {
51
-                return false;
52
-            }
53
-            // Since this is an xname, all components are supposed to allow this property
54
-            $xname = true;
55
-        }
56
-
57
-        // Create a property object of the correct class
58
-        if($xname) {
59
-            $property = new iCalendar_property_x;
60
-            $property->set_name($name);
61
-        }
62
-        else {
63
-            $classname = 'iCalendar_property_'.strtolower(str_replace('-', '_', $name));
64
-            $property = new $classname;
65
-        }
66
-
67
-        // If $value is NULL, then this property must define a default value.
68
-        if($value === NULL) {
69
-            $value = $property->default_value();
70
-            if($value === NULL) {
71
-                return false;
72
-            }
73
-        }
74
-
75
-        // Set this property's parent component to ourselves, because some
76
-        // properties behave differently according to what component they apply to.
77
-        $property->set_parent_component($this->name);
78
-
79
-        // Set parameters before value; this helps with some properties which
80
-        // accept a VALUE parameter, and thus change their default value type.
81
-
82
-        // The parameters must be valid according to property specifications
83
-        if(!empty($parameters)) {
84
-            foreach($parameters as $paramname => $paramvalue) {
85
-                if(!$property->set_parameter($paramname, $paramvalue)) {
86
-                    return false;
87
-                }
88
-            }
89
-
90
-            // Some parameters interact among themselves (e.g. ENCODING and VALUE)
91
-            // so make sure that after the dust settles, these invariants hold true
92
-            if(!$property->invariant_holds()) {
93
-                return false;
94
-            }
95
-        }
96
-
97
-        // $value MUST be valid according to the property data type
98
-        if(!$property->set_value($value)) {
99
-            return false;
100
-        }
101
-
102
-        // If this property is restricted to only once, blindly overwrite value
103
-        if(!$xname && $this->valid_properties[$name] & RFC2445_ONCE) {
104
-            $this->properties[$name] = array($property);
105
-        }
106
-
107
-        // Otherwise add it to the instance array for this property
108
-        else {
109
-            $this->properties[$name][] = $property;
110
-        }
111
-
112
-        // Finally: after all these, does the component invariant hold?
113
-        if(!$this->invariant_holds()) {
114
-            // If not, completely undo the property addition
115
-            array_pop($this->properties[$name]);
116
-            if(empty($this->properties[$name])) {
117
-                unset($this->properties[$name]);
118
-            }
119
-            return false;
120
-        }
121
-
122
-        return true;        
17
+	var $name             = NULL;
18
+	var $properties       = NULL;
19
+	var $components       = NULL;
20
+	var $valid_properties = NULL;
21
+	var $valid_components = NULL;
22
+
23
+	function iCalendar_component() {
24
+		$this->construct();
25
+	}
26
+
27
+	function construct() {
28
+		// Initialize the components array
29
+		if(empty($this->components)) {
30
+			$this->components = array();
31
+			foreach($this->valid_components as $name) {
32
+				$this->components[$name] = array();
33
+			}
34
+		}
35
+	}
36
+
37
+	function get_name() {
38
+		return $this->name;
39
+	}
40
+
41
+	function add_property($name, $value = NULL, $parameters = NULL) {
42
+
43
+		// Uppercase first of all
44
+		$name = strtoupper($name);
45
+
46
+		// Are we trying to add a valid property?
47
+		$xname = false;
48
+		if(!isset($this->valid_properties[$name])) {
49
+			// If not, is it an x-name as per RFC 2445?
50
+			if(!rfc2445_is_xname($name)) {
51
+				return false;
52
+			}
53
+			// Since this is an xname, all components are supposed to allow this property
54
+			$xname = true;
55
+		}
56
+
57
+		// Create a property object of the correct class
58
+		if($xname) {
59
+			$property = new iCalendar_property_x;
60
+			$property->set_name($name);
61
+		}
62
+		else {
63
+			$classname = 'iCalendar_property_'.strtolower(str_replace('-', '_', $name));
64
+			$property = new $classname;
65
+		}
66
+
67
+		// If $value is NULL, then this property must define a default value.
68
+		if($value === NULL) {
69
+			$value = $property->default_value();
70
+			if($value === NULL) {
71
+				return false;
72
+			}
73
+		}
74
+
75
+		// Set this property's parent component to ourselves, because some
76
+		// properties behave differently according to what component they apply to.
77
+		$property->set_parent_component($this->name);
78
+
79
+		// Set parameters before value; this helps with some properties which
80
+		// accept a VALUE parameter, and thus change their default value type.
81
+
82
+		// The parameters must be valid according to property specifications
83
+		if(!empty($parameters)) {
84
+			foreach($parameters as $paramname => $paramvalue) {
85
+				if(!$property->set_parameter($paramname, $paramvalue)) {
86
+					return false;
87
+				}
88
+			}
89
+
90
+			// Some parameters interact among themselves (e.g. ENCODING and VALUE)
91
+			// so make sure that after the dust settles, these invariants hold true
92
+			if(!$property->invariant_holds()) {
93
+				return false;
94
+			}
95
+		}
96
+
97
+		// $value MUST be valid according to the property data type
98
+		if(!$property->set_value($value)) {
99
+			return false;
100
+		}
101
+
102
+		// If this property is restricted to only once, blindly overwrite value
103
+		if(!$xname && $this->valid_properties[$name] & RFC2445_ONCE) {
104
+			$this->properties[$name] = array($property);
105
+		}
106
+
107
+		// Otherwise add it to the instance array for this property
108
+		else {
109
+			$this->properties[$name][] = $property;
110
+		}
111
+
112
+		// Finally: after all these, does the component invariant hold?
113
+		if(!$this->invariant_holds()) {
114
+			// If not, completely undo the property addition
115
+			array_pop($this->properties[$name]);
116
+			if(empty($this->properties[$name])) {
117
+				unset($this->properties[$name]);
118
+			}
119
+			return false;
120
+		}
121
+
122
+		return true;        
123 123
         
124
-    }
125
-
126
-    function add_component($component) {
127
-
128
-        // With the detailed interface, you can add only components with this function
129
-        if(!is_object($component) || !is_subclass_of($component, 'iCalendar_component')) {
130
-            return false;
131
-        }
132
-
133
-        $name = $component->get_name();
134
-
135
-        // Only valid components as specified by this component are allowed
136
-        if(!in_array($name, $this->valid_components)) {
137
-            return false;
138
-        }
139
-
140
-        // Add it
141
-        $this->components[$name][] = $component;
142
-
143
-        return true;
144
-    }
145
-
146
-    function get_property_list($name) {
147
-    }
148
-
149
-    function invariant_holds() {
150
-        return true;
151
-    }
152
-
153
-    function is_valid() {
154
-        // If we have any child components, check that they are all valid
155
-        if(!empty($this->components)) {
156
-            foreach($this->components as $component => $instances) {
157
-                foreach($instances as $number => $instance) {
158
-                    if(!$instance->is_valid()) {
159
-                        return false;
160
-                    }
161
-                }
162
-            }
163
-        }
164
-
165
-        // Finally, check the valid property list for any mandatory properties
166
-        // that have not been set and do not have a default value
167
-        foreach($this->valid_properties as $property => $propdata) {
168
-            if(($propdata & RFC2445_REQUIRED) && empty($this->properties[$property])) {
169
-                $classname = 'iCalendar_property_'.strtolower(str_replace('-', '_', $property));
170
-                $object    = new $classname;
171
-                if($object->default_value() === NULL) {
172
-                    return false;
173
-                }
174
-                unset($object);
175
-            }
176
-        }
177
-
178
-        return true;
179
-    }
124
+	}
125
+
126
+	function add_component($component) {
127
+
128
+		// With the detailed interface, you can add only components with this function
129
+		if(!is_object($component) || !is_subclass_of($component, 'iCalendar_component')) {
130
+			return false;
131
+		}
132
+
133
+		$name = $component->get_name();
134
+
135
+		// Only valid components as specified by this component are allowed
136
+		if(!in_array($name, $this->valid_components)) {
137
+			return false;
138
+		}
139
+
140
+		// Add it
141
+		$this->components[$name][] = $component;
142
+
143
+		return true;
144
+	}
145
+
146
+	function get_property_list($name) {
147
+	}
148
+
149
+	function invariant_holds() {
150
+		return true;
151
+	}
152
+
153
+	function is_valid() {
154
+		// If we have any child components, check that they are all valid
155
+		if(!empty($this->components)) {
156
+			foreach($this->components as $component => $instances) {
157
+				foreach($instances as $number => $instance) {
158
+					if(!$instance->is_valid()) {
159
+						return false;
160
+					}
161
+				}
162
+			}
163
+		}
164
+
165
+		// Finally, check the valid property list for any mandatory properties
166
+		// that have not been set and do not have a default value
167
+		foreach($this->valid_properties as $property => $propdata) {
168
+			if(($propdata & RFC2445_REQUIRED) && empty($this->properties[$property])) {
169
+				$classname = 'iCalendar_property_'.strtolower(str_replace('-', '_', $property));
170
+				$object    = new $classname;
171
+				if($object->default_value() === NULL) {
172
+					return false;
173
+				}
174
+				unset($object);
175
+			}
176
+		}
177
+
178
+		return true;
179
+	}
180 180
     
181
-    function serialize() {
182
-        // Start tag
183
-        $string = rfc2445_fold('BEGIN:'.$this->name) . RFC2445_CRLF;
184
-
185
-        // List of properties
186
-        if(!empty($this->properties)) {
187
-            foreach($this->properties as $name => $properties) {
188
-                foreach($properties as $property) {
189
-                    $string .= $property->serialize();
190
-                }
191
-            }
192
-        }
193
-
194
-        // List of components
195
-        if(!empty($this->components)) {
196
-            foreach($this->components as $name => $components) {
197
-                foreach($components as $component) {
198
-                    $string .= $component->serialize();
199
-                }
200
-            }
201
-        }
202
-
203
-        // End tag
204
-        $string .= rfc2445_fold('END:'.$this->name) . RFC2445_CRLF;
205
-
206
-        return $string;
207
-    }
181
+	function serialize() {
182
+		// Start tag
183
+		$string = rfc2445_fold('BEGIN:'.$this->name) . RFC2445_CRLF;
184
+
185
+		// List of properties
186
+		if(!empty($this->properties)) {
187
+			foreach($this->properties as $name => $properties) {
188
+				foreach($properties as $property) {
189
+					$string .= $property->serialize();
190
+				}
191
+			}
192
+		}
193
+
194
+		// List of components
195
+		if(!empty($this->components)) {
196
+			foreach($this->components as $name => $components) {
197
+				foreach($components as $component) {
198
+					$string .= $component->serialize();
199
+				}
200
+			}
201
+		}
202
+
203
+		// End tag
204
+		$string .= rfc2445_fold('END:'.$this->name) . RFC2445_CRLF;
205
+
206
+		return $string;
207
+	}
208 208
 }
209 209
 
210 210
 class iCalendar extends iCalendar_component {
211
-    var $name = 'VCALENDAR';
212
-
213
-    function construct() {
214
-        $this->valid_properties = array(
215
-            'CALSCALE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
216
-            'METHOD'      => RFC2445_OPTIONAL | RFC2445_ONCE,
217
-            'PRODID'      => RFC2445_REQUIRED | RFC2445_ONCE,
218
-            'VERSION'     => RFC2445_REQUIRED | RFC2445_ONCE,
219
-            RFC2445_XNAME => RFC2445_OPTIONAL 
220
-        );
221
-
222
-        $this->valid_components = array(
223
-            'VEVENT'
224
-            // TODO: add support for the other component types
225
-            //, 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VTIMEZONE', 'VALARM'
226
-        );
227
-        parent::construct();
228
-    }
211
+	var $name = 'VCALENDAR';
212
+
213
+	function construct() {
214
+		$this->valid_properties = array(
215
+			'CALSCALE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
216
+			'METHOD'      => RFC2445_OPTIONAL | RFC2445_ONCE,
217
+			'PRODID'      => RFC2445_REQUIRED | RFC2445_ONCE,
218
+			'VERSION'     => RFC2445_REQUIRED | RFC2445_ONCE,
219
+			RFC2445_XNAME => RFC2445_OPTIONAL 
220
+		);
221
+
222
+		$this->valid_components = array(
223
+			'VEVENT'
224
+			// TODO: add support for the other component types
225
+			//, 'VTODO', 'VJOURNAL', 'VFREEBUSY', 'VTIMEZONE', 'VALARM'
226
+		);
227
+		parent::construct();
228
+	}
229 229
 }
230 230
 
231 231
 class iCalendar_event extends iCalendar_component {
232 232
 
233
-    var $name       = 'VEVENT';
234
-    var $properties;
233
+	var $name       = 'VEVENT';
234
+	var $properties;
235 235
     
236
-    function construct() {
236
+	function construct() {
237 237
         
238
-        $this->valid_components = array('VALARM');
239
-
240
-        $this->valid_properties = array(
241
-            'CLASS'          => RFC2445_OPTIONAL | RFC2445_ONCE,
242
-            'CREATED'        => RFC2445_OPTIONAL | RFC2445_ONCE,
243
-            'DESCRIPTION'    => RFC2445_OPTIONAL | RFC2445_ONCE,
244
-            // Standard ambiguous here: in 4.6.1 it says that DTSTAMP in optional,
245
-            // while in 4.8.7.2 it says it's REQUIRED. Go with REQUIRED.
246
-            'DTSTAMP'        => RFC2445_REQUIRED | RFC2445_ONCE,
247
-            // Standard ambiguous here: in 4.6.1 it says that DTSTART in optional,
248
-            // while in 4.8.2.4 it says it's REQUIRED. Go with REQUIRED.
249
-            'DTSTART'        => RFC2445_REQUIRED | RFC2445_ONCE,
250
-            'GEO'            => RFC2445_OPTIONAL | RFC2445_ONCE,
251
-            'LAST-MODIFIED'  => RFC2445_OPTIONAL | RFC2445_ONCE,
252
-            'LOCATION'       => RFC2445_OPTIONAL | RFC2445_ONCE,
253
-            'ORGANIZER'      => RFC2445_OPTIONAL | RFC2445_ONCE,
254
-            'PRIORITY'       => RFC2445_OPTIONAL | RFC2445_ONCE,
255
-            'SEQUENCE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
256
-            'STATUS'         => RFC2445_OPTIONAL | RFC2445_ONCE,
257
-            'SUMMARY'        => RFC2445_OPTIONAL | RFC2445_ONCE,
258
-            'TRANSP'         => RFC2445_OPTIONAL | RFC2445_ONCE,
259
-            // Standard ambiguous here: in 4.6.1 it says that UID in optional,
260
-            // while in 4.8.4.7 it says it's REQUIRED. Go with REQUIRED.
261
-            'UID'            => RFC2445_REQUIRED | RFC2445_ONCE,
262
-            'URL'            => RFC2445_OPTIONAL | RFC2445_ONCE,
263
-            'RECURRENCE-ID'  => RFC2445_OPTIONAL | RFC2445_ONCE,
264
-            'DTEND'          => RFC2445_OPTIONAL | RFC2445_ONCE,
265
-            'DURATION'       => RFC2445_OPTIONAL | RFC2445_ONCE,
266
-            'ATTACH'         => RFC2445_OPTIONAL,
267
-            'ATTENDEE'       => RFC2445_OPTIONAL,
268
-            'CATEGORIES'     => RFC2445_OPTIONAL,
269
-            'COMMENT'        => RFC2445_OPTIONAL,
270
-            'CONTACT'        => RFC2445_OPTIONAL,
271
-            'EXDATE'         => RFC2445_OPTIONAL,
272
-            'EXRULE'         => RFC2445_OPTIONAL,
273
-            'REQUEST-STATUS' => RFC2445_OPTIONAL,
274
-            'RELATED-TO'     => RFC2445_OPTIONAL,
275
-            'RESOURCES'      => RFC2445_OPTIONAL,
276
-            'RDATE'          => RFC2445_OPTIONAL,
277
-            'RRULE'          => RFC2445_OPTIONAL,
278
-            RFC2445_XNAME    => RFC2445_OPTIONAL
279
-        );
280
-
281
-        parent::construct();
282
-    }
283
-
284
-    function invariant_holds() {
285
-        // DTEND and DURATION must not appear together
286
-        if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) {
287
-            return false;
288
-        }
238
+		$this->valid_components = array('VALARM');
239
+
240
+		$this->valid_properties = array(
241
+			'CLASS'          => RFC2445_OPTIONAL | RFC2445_ONCE,
242
+			'CREATED'        => RFC2445_OPTIONAL | RFC2445_ONCE,
243
+			'DESCRIPTION'    => RFC2445_OPTIONAL | RFC2445_ONCE,
244
+			// Standard ambiguous here: in 4.6.1 it says that DTSTAMP in optional,
245
+			// while in 4.8.7.2 it says it's REQUIRED. Go with REQUIRED.
246
+			'DTSTAMP'        => RFC2445_REQUIRED | RFC2445_ONCE,
247
+			// Standard ambiguous here: in 4.6.1 it says that DTSTART in optional,
248
+			// while in 4.8.2.4 it says it's REQUIRED. Go with REQUIRED.
249
+			'DTSTART'        => RFC2445_REQUIRED | RFC2445_ONCE,
250
+			'GEO'            => RFC2445_OPTIONAL | RFC2445_ONCE,
251
+			'LAST-MODIFIED'  => RFC2445_OPTIONAL | RFC2445_ONCE,
252
+			'LOCATION'       => RFC2445_OPTIONAL | RFC2445_ONCE,
253
+			'ORGANIZER'      => RFC2445_OPTIONAL | RFC2445_ONCE,
254
+			'PRIORITY'       => RFC2445_OPTIONAL | RFC2445_ONCE,
255
+			'SEQUENCE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
256
+			'STATUS'         => RFC2445_OPTIONAL | RFC2445_ONCE,
257
+			'SUMMARY'        => RFC2445_OPTIONAL | RFC2445_ONCE,
258
+			'TRANSP'         => RFC2445_OPTIONAL | RFC2445_ONCE,
259
+			// Standard ambiguous here: in 4.6.1 it says that UID in optional,
260
+			// while in 4.8.4.7 it says it's REQUIRED. Go with REQUIRED.
261
+			'UID'            => RFC2445_REQUIRED | RFC2445_ONCE,
262
+			'URL'            => RFC2445_OPTIONAL | RFC2445_ONCE,
263
+			'RECURRENCE-ID'  => RFC2445_OPTIONAL | RFC2445_ONCE,
264
+			'DTEND'          => RFC2445_OPTIONAL | RFC2445_ONCE,
265
+			'DURATION'       => RFC2445_OPTIONAL | RFC2445_ONCE,
266
+			'ATTACH'         => RFC2445_OPTIONAL,
267
+			'ATTENDEE'       => RFC2445_OPTIONAL,
268
+			'CATEGORIES'     => RFC2445_OPTIONAL,
269
+			'COMMENT'        => RFC2445_OPTIONAL,
270
+			'CONTACT'        => RFC2445_OPTIONAL,
271
+			'EXDATE'         => RFC2445_OPTIONAL,
272
+			'EXRULE'         => RFC2445_OPTIONAL,
273
+			'REQUEST-STATUS' => RFC2445_OPTIONAL,
274
+			'RELATED-TO'     => RFC2445_OPTIONAL,
275
+			'RESOURCES'      => RFC2445_OPTIONAL,
276
+			'RDATE'          => RFC2445_OPTIONAL,
277
+			'RRULE'          => RFC2445_OPTIONAL,
278
+			RFC2445_XNAME    => RFC2445_OPTIONAL
279
+		);
280
+
281
+		parent::construct();
282
+	}
283
+
284
+	function invariant_holds() {
285
+		// DTEND and DURATION must not appear together
286
+		if(isset($this->properties['DTEND']) && isset($this->properties['DURATION'])) {
287
+			return false;
288
+		}
289 289
 
290 290
         
291
-        if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) {
292
-            // DTEND must be later than DTSTART
293
-            // The standard is not clear on how to hande different value types though
294
-            // TODO: handle this correctly even if the value types are different
295
-            if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) {
296
-                return false;
297
-            }
298
-
299
-            // DTEND and DTSTART must have the same value type
300
-            if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) {
301
-                return false;
302
-            }
303
-
304
-        }
305
-        return true;
306
-    }
291
+		if(isset($this->properties['DTEND']) && isset($this->properties['DTSTART'])) {
292
+			// DTEND must be later than DTSTART
293
+			// The standard is not clear on how to hande different value types though
294
+			// TODO: handle this correctly even if the value types are different
295
+			if($this->properties['DTEND'][0]->value <= $this->properties['DTSTART'][0]->value) {
296
+				return false;
297
+			}
298
+
299
+			// DTEND and DTSTART must have the same value type
300
+			if($this->properties['DTEND'][0]->val_type != $this->properties['DTSTART'][0]->val_type) {
301
+				return false;
302
+			}
303
+
304
+		}
305
+		return true;
306
+	}
307 307
 }
308 308
 
309 309
 class iCalendar_todo extends iCalendar_component {
310
-    var $name       = 'VTODO';
311
-    var $properties;
312
-
313
-    function construct() {
314
-
315
-        $this->properties = array(
316
-            'class'       => RFC2445_OPTIONAL | RFC2445_ONCE,
317
-            'completed'   => RFC2445_OPTIONAL | RFC2445_ONCE,
318
-            'created'     => RFC2445_OPTIONAL | RFC2445_ONCE,
319
-            'description' => RFC2445_OPTIONAL | RFC2445_ONCE,
320
-            'dtstamp'     => RFC2445_OPTIONAL | RFC2445_ONCE,
321
-            'dtstart'     => RFC2445_OPTIONAL | RFC2445_ONCE,
322
-            'geo'         => RFC2445_OPTIONAL | RFC2445_ONCE,
323
-            'last-modified'    => RFC2445_OPTIONAL | RFC2445_ONCE,
324
-            'location'    => RFC2445_OPTIONAL | RFC2445_ONCE,
325
-            'organizer'   => RFC2445_OPTIONAL | RFC2445_ONCE,
326
-            'percent'     => RFC2445_OPTIONAL | RFC2445_ONCE,
327
-            'priority'    => RFC2445_OPTIONAL | RFC2445_ONCE,
328
-            'recurid'     => RFC2445_OPTIONAL | RFC2445_ONCE,
329
-            'sequence'    => RFC2445_OPTIONAL | RFC2445_ONCE,
330
-            'status'      => RFC2445_OPTIONAL | RFC2445_ONCE,
331
-            'summary'     => RFC2445_OPTIONAL | RFC2445_ONCE,
332
-            'uid'         => RFC2445_OPTIONAL | RFC2445_ONCE,
333
-            'url'         => RFC2445_OPTIONAL | RFC2445_ONCE,
334
-            'due'         => RFC2445_OPTIONAL | RFC2445_ONCE,
335
-            'duration'    => RFC2445_OPTIONAL | RFC2445_ONCE,
336
-            'attach'      => RFC2445_OPTIONAL,
337
-            'attendee'    => RFC2445_OPTIONAL,
338
-            'categories'  => RFC2445_OPTIONAL,
339
-            'comment'     => RFC2445_OPTIONAL,
340
-            'contact'     => RFC2445_OPTIONAL,
341
-            'exdate'      => RFC2445_OPTIONAL,
342
-            'exrule'      => RFC2445_OPTIONAL,
343
-            'rstatus'     => RFC2445_OPTIONAL,
344
-            'related'     => RFC2445_OPTIONAL,
345
-            'resources'   => RFC2445_OPTIONAL,
346
-            'rdate'       => RFC2445_OPTIONAL,
347
-            'rrule'       => RFC2445_OPTIONAL,
348
-            'xprop'       => RFC2445_OPTIONAL
349
-        );
350
-
351
-        parent::construct();
352
-        // TODO:
353
-        // either 'due' or 'duration' may appear in  a 'eventprop', but 'due'
354
-        // and 'duration' MUST NOT occur in the same 'eventprop'
355
-    }
310
+	var $name       = 'VTODO';
311
+	var $properties;
312
+
313
+	function construct() {
314
+
315
+		$this->properties = array(
316
+			'class'       => RFC2445_OPTIONAL | RFC2445_ONCE,
317
+			'completed'   => RFC2445_OPTIONAL | RFC2445_ONCE,
318
+			'created'     => RFC2445_OPTIONAL | RFC2445_ONCE,
319
+			'description' => RFC2445_OPTIONAL | RFC2445_ONCE,
320
+			'dtstamp'     => RFC2445_OPTIONAL | RFC2445_ONCE,
321
+			'dtstart'     => RFC2445_OPTIONAL | RFC2445_ONCE,
322
+			'geo'         => RFC2445_OPTIONAL | RFC2445_ONCE,
323
+			'last-modified'    => RFC2445_OPTIONAL | RFC2445_ONCE,
324
+			'location'    => RFC2445_OPTIONAL | RFC2445_ONCE,
325
+			'organizer'   => RFC2445_OPTIONAL | RFC2445_ONCE,
326
+			'percent'     => RFC2445_OPTIONAL | RFC2445_ONCE,
327
+			'priority'    => RFC2445_OPTIONAL | RFC2445_ONCE,
328
+			'recurid'     => RFC2445_OPTIONAL | RFC2445_ONCE,
329
+			'sequence'    => RFC2445_OPTIONAL | RFC2445_ONCE,
330
+			'status'      => RFC2445_OPTIONAL | RFC2445_ONCE,
331
+			'summary'     => RFC2445_OPTIONAL | RFC2445_ONCE,
332
+			'uid'         => RFC2445_OPTIONAL | RFC2445_ONCE,
333
+			'url'         => RFC2445_OPTIONAL | RFC2445_ONCE,
334
+			'due'         => RFC2445_OPTIONAL | RFC2445_ONCE,
335
+			'duration'    => RFC2445_OPTIONAL | RFC2445_ONCE,
336
+			'attach'      => RFC2445_OPTIONAL,
337
+			'attendee'    => RFC2445_OPTIONAL,
338
+			'categories'  => RFC2445_OPTIONAL,
339
+			'comment'     => RFC2445_OPTIONAL,
340
+			'contact'     => RFC2445_OPTIONAL,
341
+			'exdate'      => RFC2445_OPTIONAL,
342
+			'exrule'      => RFC2445_OPTIONAL,
343
+			'rstatus'     => RFC2445_OPTIONAL,
344
+			'related'     => RFC2445_OPTIONAL,
345
+			'resources'   => RFC2445_OPTIONAL,
346
+			'rdate'       => RFC2445_OPTIONAL,
347
+			'rrule'       => RFC2445_OPTIONAL,
348
+			'xprop'       => RFC2445_OPTIONAL
349
+		);
350
+
351
+		parent::construct();
352
+		// TODO:
353
+		// either 'due' or 'duration' may appear in  a 'eventprop', but 'due'
354
+		// and 'duration' MUST NOT occur in the same 'eventprop'
355
+	}
356 356
 }
357 357
 
358 358
 class iCalendar_journal extends iCalendar_component {
359
-    // TODO: implement
359
+	// TODO: implement
360 360
 }
361 361
 
362 362
 class iCalendar_freebusy extends iCalendar_component {
363
-    // TODO: implement
363
+	// TODO: implement
364 364
 }
365 365
 
366 366
 class iCalendar_alarm extends iCalendar_component {
367
-    // TODO: implement
367
+	// TODO: implement
368 368
 }
369 369
 
370 370
 class iCalendar_timezone extends iCalendar_component {
371
-    var $name       = 'VTIMEZONE';
372
-    var $properties;
373
-
374
-    function construct() {
375
-
376
-        $this->properties = array(
377
-            'tzid'        => RFC2445_REQUIRED | RFC2445_ONCE,
378
-            'last-modified'    => RFC2445_OPTIONAL | RFC2445_ONCE,
379
-            'tzurl'       => RFC2445_OPTIONAL | RFC2445_ONCE,
380
-            // TODO: the next two are components of their own!
381
-            'standardc'   => RFC2445_REQUIRED,
382
-            'daylightc'   => RFC2445_REQUIRED,
383
-            'x-prop'      => RFC2445_OPTIONAL
384
-        );
371
+	var $name       = 'VTIMEZONE';
372
+	var $properties;
373
+
374
+	function construct() {
375
+
376
+		$this->properties = array(
377
+			'tzid'        => RFC2445_REQUIRED | RFC2445_ONCE,
378
+			'last-modified'    => RFC2445_OPTIONAL | RFC2445_ONCE,
379
+			'tzurl'       => RFC2445_OPTIONAL | RFC2445_ONCE,
380
+			// TODO: the next two are components of their own!
381
+			'standardc'   => RFC2445_REQUIRED,
382
+			'daylightc'   => RFC2445_REQUIRED,
383
+			'x-prop'      => RFC2445_OPTIONAL
384
+		);
385 385
         
386
-        parent::construct();
387
-    }
386
+		parent::construct();
387
+	}
388 388
 }
389 389
 
390 390
 // REMINDER: DTEND must be later than DTSTART for all components which support both
Please login to merge, or discard this patch.
phpicalendar/lib/bennu/iCalendar_parameters.php 1 patch
Indentation   +220 added lines, -220 removed lines patch added patch discarded remove patch
@@ -14,226 +14,226 @@
 block discarded – undo
14 14
  */
15 15
 
16 16
 class iCalendar_parameter {
17
-    function multiple_values_allowed($parameter) {
18
-        switch($parameter) {
19
-            case 'DELEGATED-FROM':
20
-            case 'DELEGATED-TO':
21
-            case 'MEMBER':
22
-                return true;
23
-            default:
24
-                return false;
25
-        }
26
-    }
27
-
28
-    function default_value($parameter) {
29
-        switch($parameter) {
30
-            case 'CUTYPE':   return 'INDIVIDUAL';
31
-            case 'FBTYPE':   return 'BUSY';
32
-            case 'PARTSTAT': return 'NEEDS-ACTION';
33
-            case 'RELATED':  return 'START';
34
-            case 'RELTYPE':  return 'PARENT';
35
-            case 'ROLE':     return 'REQ-PARTICIPANT';
36
-            case 'RSVP':     return 'FALSE';
37
-            default:         return NULL;
38
-        }
39
-    }
40
-
41
-    function is_valid_value(&$parent_property, $parameter, $value) {
42
-        switch($parameter) {
43
-            // These must all be a URI
44
-            case 'ALTREP':
45
-            case 'DIR':
46
-                return rfc2445_is_valid_value($value, RFC2445_TYPE_URI);
47
-            break;
48
-
49
-            // These must be CAL-ADDRESS, which is equivalent to URI
50
-            case 'DELEGATED-FROM':
51
-            case 'DELEGATED-TO':
52
-            case 'MEMBER':
53
-            case 'SENT-BY':
54
-                return rfc2445_is_valid_value($value, RFC2445_TYPE_CAL_ADDRESS);
55
-            break;
56
-
57
-            // These are textual parameters, so the MUST NOT contain double quotes
58
-            case 'CN':
59
-                return (strpos($value, '"') === false);
60
-            break;
61
-
62
-            // These have enumerated legal values
63
-            case 'CUTYPE':
64
-                $value = strtoupper($value);
65
-                return ($value == 'INDIVIDUAL' || $value == 'GROUP' || $value == 'RESOURCE' || $value == 'ROOM' || $value == 'UNKNOWN' || rfc2445_is_xname($value));
66
-            break;
67
-
68
-            case 'ENCODING':
69
-                $value = strtoupper($value);
70
-                return ($value == '8BIT' || $value == 'BASE64' || rfc2445_is_xname($value));
71
-            break;
72
-
73
-            case 'FBTYPE':
74
-                $value = strtoupper($value);
75
-                return ($value == 'FREE' || $value == 'BUSY' || $value == 'BUSY-UNAVAILABLE' || $value == 'BUSY-TENTATIVE' || rfc2445_is_xname($value));
76
-            break;
77
-
78
-            case 'FMTTYPE':
79
-                $fmttypes = array(
80
-                        'TEXT'        => array('PLAIN', 'RICHTEXT', 'ENRICHED', 'TAB-SEPARATED-VALUES', 'HTML', 'SGML',
81
-                                               'VND.LATEX-Z', 'VND.FMI.FLEXSTOR'),
82
-                        'MULTIPART'   => array('MIXED', 'ALTERNATIVE', 'DIGEST', 'PARALLEL', 'APPLEDOUBLE', 'HEADER-SET',
83
-                                               'FORM-DATA', 'RELATED', 'REPORT', 'VOICE-MESSAGE', 'SIGNED', 'ENCRYPTED',
84
-                                               'BYTERANGES'),
85
-                        'MESSAGE'     => array('RFC822', 'PARTIAL', 'EXTERNAL-BODY', 'NEWS', 'HTTP'),
86
-                        'APPLICATION' => array('OCTET-STREAM', 'POSTSCRIPT', 'ODA', 'ATOMICMAIL', 'ANDREW-INSET', 'SLATE',
87
-                                               'WITA', 'DEC-DX', 'DCA-RFT', 'ACTIVEMESSAGE', 'RTF', 'APPLEFILE',
88
-                                               'MAC-BINHEX40', 'NEWS-MESSAGE-ID', 'NEWS-TRANSMISSION', 'WORDPERFECT5.1',
89
-                                               'PDF', 'ZIP', 'MACWRITEII', 'MSWORD', 'REMOTE-PRINTING', 'MATHEMATICA',
90
-                                               'CYBERCASH', 'COMMONGROUND', 'IGES', 'RISCOS', 'ESHOP', 'X400-BP', 'SGML',
91
-                                               'CALS-1840', 'PGP-ENCRYPTED', 'PGP-SIGNATURE', 'PGP-KEYS', 'VND.FRAMEMAKER',
92
-                                               'VND.MIF', 'VND.MS-EXCEL', 'VND.MS-POWERPOINT', 'VND.MS-PROJECT',
93
-                                               'VND.MS-WORKS', 'VND.MS-TNEF', 'VND.SVD', 'VND.MUSIC-NIFF', 'VND.MS-ARTGALRY',
94
-                                               'VND.TRUEDOC', 'VND.KOAN', 'VND.STREET-STREAM', 'VND.FDF',
95
-                                               'SET-PAYMENT-INITIATION', 'SET-PAYMENT', 'SET-REGISTRATION-INITIATION',
96
-                                               'SET-REGISTRATION', 'VND.SEEMAIL', 'VND.BUSINESSOBJECTS',
97
-                                               'VND.MERIDIAN-SLINGSHOT', 'VND.XARA', 'SGML-OPEN-CATALOG', 'VND.RAPID',
98
-                                               'VND.ENLIVEN', 'VND.JAPANNET-REGISTRATION-WAKEUP', 
99
-                                               'VND.JAPANNET-VERIFICATION-WAKEUP', 'VND.JAPANNET-PAYMENT-WAKEUP',
100
-                                               'VND.JAPANNET-DIRECTORY-SERVICE', 'VND.INTERTRUST.DIGIBOX', 'VND.INTERTRUST.NNCP'),
101
-                        'IMAGE'       => array('JPEG', 'GIF', 'IEF', 'G3FAX', 'TIFF', 'CGM', 'NAPLPS', 'VND.DWG', 'VND.SVF',
102
-                                               'VND.DXF', 'PNG', 'VND.FPX', 'VND.NET-FPX'),
103
-                        'AUDIO'       => array('BASIC', '32KADPCM', 'VND.QCELP'),
104
-                        'VIDEO'       => array('MPEG', 'QUICKTIME', 'VND.VIVO', 'VND.MOTOROLA.VIDEO', 'VND.MOTOROLA.VIDEOP')
105
-                );
106
-                $value = strtoupper($value);
107
-                if(rfc2445_is_xname($value)) {
108
-                    return true;
109
-                }
110
-                @list($type, $subtype) = explode('/', $value);
111
-                if(empty($type) || empty($subtype)) {
112
-                    return false;
113
-                }
114
-                if(!isset($fmttypes[$type]) || !in_array($subtype, $fmttypes[$type])) {
115
-                    return false;
116
-                }
117
-                return true;
118
-            break;
119
-
120
-            case 'LANGUAGE':
121
-                $value = strtoupper($value);
122
-                $parts = explode('-', $value);
123
-                foreach($parts as $part) {
124
-                    if(empty($part)) {
125
-                        return false;
126
-                    }
127
-                    if(strspn($part, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') != strlen($part)) {
128
-                        return false;
129
-                    }
130
-                }
131
-                return true;
132
-            break;
133
-
134
-            case 'PARTSTAT':
135
-                $value = strtoupper($value);
136
-                switch($parent_property->parent_component) {
137
-                    case 'VEVENT':
138
-                        return ($value == 'NEEDS-ACTION' || $value == 'ACCEPTED' || $value == 'DECLINED' || $value == 'TENTATIVE'
139
-                                || $value == 'DELEGATED' || rfc2445_is_xname($value));
140
-                    break;
141
-                    case 'VTODO':
142
-                        return ($value == 'NEEDS-ACTION' || $value == 'ACCEPTED' || $value == 'DECLINED' || $value == 'TENTATIVE'
143
-                                || $value == 'DELEGATED' || $value == 'COMPLETED' || $value == 'IN-PROCESS' || rfc2445_is_xname($value));
144
-                    break;
145
-                    case 'VJOURNAL':
146
-                        return ($value == 'NEEDS-ACTION' || $value == 'ACCEPTED' || $value == 'DECLINED' || rfc2445_is_xname($value));
147
-                    break;
148
-                }
149
-                return false;
150
-            break;
151
-
152
-            case 'RANGE':
153
-                $value = strtoupper($value);
154
-                return ($value == 'THISANDPRIOR' || $value == 'THISANDFUTURE');
155
-            break;
156
-
157
-            case 'RELATED':
158
-                $value = strtoupper($value);
159
-                return ($value == 'START' || $value == 'END');
160
-            break;
161
-
162
-            case 'RELTYPE':
163
-                $value = strtoupper($value);
164
-                return ($value == 'PARENT' || $value == 'CHILD' || $value == 'SIBLING' || rfc2445_is_xname($value));
165
-            break;
166
-
167
-            case 'ROLE':
168
-                $value = strtoupper($value);
169
-                return ($value == 'CHAIR' || $value == 'REQ-PARTICIPANT' || $value == 'OPT-PARTICIPANT' || $value == 'NON-PARTICIPANT' || rfc2445_is_xname($value));
170
-            break;
171
-
172
-            case 'RSVP':
173
-                $value = strtoupper($value);
174
-                return ($value == 'TRUE' || $value == 'FALSE');
175
-            break;
176
-
177
-            case 'TZID':
178
-                if(empty($value)) {
179
-                    return false;
180
-                }
181
-                return (strcspn($value, '";:,') == strlen($value));
182
-            break;
183
-
184
-            case 'VALUE':
185
-                $value = strtoupper($value);
186
-                return ($value == 'BINARY'    || $value == 'BOOLEAN'    || $value == 'CAL-ADDRESS' || $value == 'DATE'    ||
187
-                        $value == 'DATE-TIME' || $value == 'DURATION'   || $value == 'FLOAT'       || $value == 'INTEGER' ||
188
-                        $value == 'PERIOD'    || $value == 'RECUR'      || $value == 'TEXT'        || $value == 'TIME'    ||
189
-                        $value == 'URI'       || $value == 'UTC-OFFSET' || rfc2445_is_xname($value));
190
-            break;
191
-        }
192
-    }
193
-
194
-    function do_value_formatting($parameter, $value) {
195
-        switch($parameter) {
196
-            // Parameters of type CAL-ADDRESS or URI MUST be double-quoted
197
-            case 'ALTREP':
198
-            case 'DIR':
199
-            case 'DELEGATED-FROM':
200
-            case 'DELEGATED-TO':
201
-            case 'MEMBER':
202
-            case 'SENT-BY':
203
-                return '"'.$value.'"';
204
-            break;
205
-
206
-            // Textual parameter types must be double quoted if they contain COLON, SEMICOLON
207
-            // or COMMA. Quoting always sounds easier and standards-conformant though.
208
-            case 'CN':
209
-                return '"'.$value.'"';
210
-            break;
211
-
212
-            // Parameters with enumerated legal values, just make them all caps
213
-            case 'CUTYPE':
214
-            case 'ENCODING':
215
-            case 'FBTYPE':
216
-            case 'FMTTYPE':
217
-            case 'LANGUAGE':
218
-            case 'PARTSTAT':
219
-            case 'RANGE':
220
-            case 'RELATED':
221
-            case 'RELTYPE':
222
-            case 'ROLE':
223
-            case 'RSVP':
224
-            case 'VALUE':
225
-                return strtoupper($value);
226
-            break;
227
-
228
-            // Parameters we shouldn't be messing with
229
-            case 'TZID':
230
-                return $value;
231
-            break;
232
-        }
233
-    }
234
-
235
-    function undo_value_formatting($parameter, $value) {
236
-    }
17
+	function multiple_values_allowed($parameter) {
18
+		switch($parameter) {
19
+			case 'DELEGATED-FROM':
20
+			case 'DELEGATED-TO':
21
+			case 'MEMBER':
22
+				return true;
23
+			default:
24
+				return false;
25
+		}
26
+	}
27
+
28
+	function default_value($parameter) {
29
+		switch($parameter) {
30
+			case 'CUTYPE':   return 'INDIVIDUAL';
31
+			case 'FBTYPE':   return 'BUSY';
32
+			case 'PARTSTAT': return 'NEEDS-ACTION';
33
+			case 'RELATED':  return 'START';
34
+			case 'RELTYPE':  return 'PARENT';
35
+			case 'ROLE':     return 'REQ-PARTICIPANT';
36
+			case 'RSVP':     return 'FALSE';
37
+			default:         return NULL;
38
+		}
39
+	}
40
+
41
+	function is_valid_value(&$parent_property, $parameter, $value) {
42
+		switch($parameter) {
43
+			// These must all be a URI
44
+			case 'ALTREP':
45
+			case 'DIR':
46
+				return rfc2445_is_valid_value($value, RFC2445_TYPE_URI);
47
+			break;
48
+
49
+			// These must be CAL-ADDRESS, which is equivalent to URI
50
+			case 'DELEGATED-FROM':
51
+			case 'DELEGATED-TO':
52
+			case 'MEMBER':
53
+			case 'SENT-BY':
54
+				return rfc2445_is_valid_value($value, RFC2445_TYPE_CAL_ADDRESS);
55
+			break;
56
+
57
+			// These are textual parameters, so the MUST NOT contain double quotes
58
+			case 'CN':
59
+				return (strpos($value, '"') === false);
60
+			break;
61
+
62
+			// These have enumerated legal values
63
+			case 'CUTYPE':
64
+				$value = strtoupper($value);
65
+				return ($value == 'INDIVIDUAL' || $value == 'GROUP' || $value == 'RESOURCE' || $value == 'ROOM' || $value == 'UNKNOWN' || rfc2445_is_xname($value));
66
+			break;
67
+
68
+			case 'ENCODING':
69
+				$value = strtoupper($value);
70
+				return ($value == '8BIT' || $value == 'BASE64' || rfc2445_is_xname($value));
71
+			break;
72
+
73
+			case 'FBTYPE':
74
+				$value = strtoupper($value);
75
+				return ($value == 'FREE' || $value == 'BUSY' || $value == 'BUSY-UNAVAILABLE' || $value == 'BUSY-TENTATIVE' || rfc2445_is_xname($value));
76
+			break;
77
+
78
+			case 'FMTTYPE':
79
+				$fmttypes = array(
80
+						'TEXT'        => array('PLAIN', 'RICHTEXT', 'ENRICHED', 'TAB-SEPARATED-VALUES', 'HTML', 'SGML',
81
+											   'VND.LATEX-Z', 'VND.FMI.FLEXSTOR'),
82
+						'MULTIPART'   => array('MIXED', 'ALTERNATIVE', 'DIGEST', 'PARALLEL', 'APPLEDOUBLE', 'HEADER-SET',
83
+											   'FORM-DATA', 'RELATED', 'REPORT', 'VOICE-MESSAGE', 'SIGNED', 'ENCRYPTED',
84
+											   'BYTERANGES'),
85
+						'MESSAGE'     => array('RFC822', 'PARTIAL', 'EXTERNAL-BODY', 'NEWS', 'HTTP'),
86
+						'APPLICATION' => array('OCTET-STREAM', 'POSTSCRIPT', 'ODA', 'ATOMICMAIL', 'ANDREW-INSET', 'SLATE',
87
+											   'WITA', 'DEC-DX', 'DCA-RFT', 'ACTIVEMESSAGE', 'RTF', 'APPLEFILE',
88
+											   'MAC-BINHEX40', 'NEWS-MESSAGE-ID', 'NEWS-TRANSMISSION', 'WORDPERFECT5.1',
89
+											   'PDF', 'ZIP', 'MACWRITEII', 'MSWORD', 'REMOTE-PRINTING', 'MATHEMATICA',
90
+											   'CYBERCASH', 'COMMONGROUND', 'IGES', 'RISCOS', 'ESHOP', 'X400-BP', 'SGML',
91
+											   'CALS-1840', 'PGP-ENCRYPTED', 'PGP-SIGNATURE', 'PGP-KEYS', 'VND.FRAMEMAKER',
92
+											   'VND.MIF', 'VND.MS-EXCEL', 'VND.MS-POWERPOINT', 'VND.MS-PROJECT',
93
+											   'VND.MS-WORKS', 'VND.MS-TNEF', 'VND.SVD', 'VND.MUSIC-NIFF', 'VND.MS-ARTGALRY',
94
+											   'VND.TRUEDOC', 'VND.KOAN', 'VND.STREET-STREAM', 'VND.FDF',
95
+											   'SET-PAYMENT-INITIATION', 'SET-PAYMENT', 'SET-REGISTRATION-INITIATION',
96
+											   'SET-REGISTRATION', 'VND.SEEMAIL', 'VND.BUSINESSOBJECTS',
97
+											   'VND.MERIDIAN-SLINGSHOT', 'VND.XARA', 'SGML-OPEN-CATALOG', 'VND.RAPID',
98
+											   'VND.ENLIVEN', 'VND.JAPANNET-REGISTRATION-WAKEUP', 
99
+											   'VND.JAPANNET-VERIFICATION-WAKEUP', 'VND.JAPANNET-PAYMENT-WAKEUP',
100
+											   'VND.JAPANNET-DIRECTORY-SERVICE', 'VND.INTERTRUST.DIGIBOX', 'VND.INTERTRUST.NNCP'),
101
+						'IMAGE'       => array('JPEG', 'GIF', 'IEF', 'G3FAX', 'TIFF', 'CGM', 'NAPLPS', 'VND.DWG', 'VND.SVF',
102
+											   'VND.DXF', 'PNG', 'VND.FPX', 'VND.NET-FPX'),
103
+						'AUDIO'       => array('BASIC', '32KADPCM', 'VND.QCELP'),
104
+						'VIDEO'       => array('MPEG', 'QUICKTIME', 'VND.VIVO', 'VND.MOTOROLA.VIDEO', 'VND.MOTOROLA.VIDEOP')
105
+				);
106
+				$value = strtoupper($value);
107
+				if(rfc2445_is_xname($value)) {
108
+					return true;
109
+				}
110
+				@list($type, $subtype) = explode('/', $value);
111
+				if(empty($type) || empty($subtype)) {
112
+					return false;
113
+				}
114
+				if(!isset($fmttypes[$type]) || !in_array($subtype, $fmttypes[$type])) {
115
+					return false;
116
+				}
117
+				return true;
118
+			break;
119
+
120
+			case 'LANGUAGE':
121
+				$value = strtoupper($value);
122
+				$parts = explode('-', $value);
123
+				foreach($parts as $part) {
124
+					if(empty($part)) {
125
+						return false;
126
+					}
127
+					if(strspn($part, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') != strlen($part)) {
128
+						return false;
129
+					}
130
+				}
131
+				return true;
132
+			break;
133
+
134
+			case 'PARTSTAT':
135
+				$value = strtoupper($value);
136
+				switch($parent_property->parent_component) {
137
+					case 'VEVENT':
138
+						return ($value == 'NEEDS-ACTION' || $value == 'ACCEPTED' || $value == 'DECLINED' || $value == 'TENTATIVE'
139
+								|| $value == 'DELEGATED' || rfc2445_is_xname($value));
140
+					break;
141
+					case 'VTODO':
142
+						return ($value == 'NEEDS-ACTION' || $value == 'ACCEPTED' || $value == 'DECLINED' || $value == 'TENTATIVE'
143
+								|| $value == 'DELEGATED' || $value == 'COMPLETED' || $value == 'IN-PROCESS' || rfc2445_is_xname($value));
144
+					break;
145
+					case 'VJOURNAL':
146
+						return ($value == 'NEEDS-ACTION' || $value == 'ACCEPTED' || $value == 'DECLINED' || rfc2445_is_xname($value));
147
+					break;
148
+				}
149
+				return false;
150
+			break;
151
+
152
+			case 'RANGE':
153
+				$value = strtoupper($value);
154
+				return ($value == 'THISANDPRIOR' || $value == 'THISANDFUTURE');
155
+			break;
156
+
157
+			case 'RELATED':
158
+				$value = strtoupper($value);
159
+				return ($value == 'START' || $value == 'END');
160
+			break;
161
+
162
+			case 'RELTYPE':
163
+				$value = strtoupper($value);
164
+				return ($value == 'PARENT' || $value == 'CHILD' || $value == 'SIBLING' || rfc2445_is_xname($value));
165
+			break;
166
+
167
+			case 'ROLE':
168
+				$value = strtoupper($value);
169
+				return ($value == 'CHAIR' || $value == 'REQ-PARTICIPANT' || $value == 'OPT-PARTICIPANT' || $value == 'NON-PARTICIPANT' || rfc2445_is_xname($value));
170
+			break;
171
+
172
+			case 'RSVP':
173
+				$value = strtoupper($value);
174
+				return ($value == 'TRUE' || $value == 'FALSE');
175
+			break;
176
+
177
+			case 'TZID':
178
+				if(empty($value)) {
179
+					return false;
180
+				}
181
+				return (strcspn($value, '";:,') == strlen($value));
182
+			break;
183
+
184
+			case 'VALUE':
185
+				$value = strtoupper($value);
186
+				return ($value == 'BINARY'    || $value == 'BOOLEAN'    || $value == 'CAL-ADDRESS' || $value == 'DATE'    ||
187
+						$value == 'DATE-TIME' || $value == 'DURATION'   || $value == 'FLOAT'       || $value == 'INTEGER' ||
188
+						$value == 'PERIOD'    || $value == 'RECUR'      || $value == 'TEXT'        || $value == 'TIME'    ||
189
+						$value == 'URI'       || $value == 'UTC-OFFSET' || rfc2445_is_xname($value));
190
+			break;
191
+		}
192
+	}
193
+
194
+	function do_value_formatting($parameter, $value) {
195
+		switch($parameter) {
196
+			// Parameters of type CAL-ADDRESS or URI MUST be double-quoted
197
+			case 'ALTREP':
198
+			case 'DIR':
199
+			case 'DELEGATED-FROM':
200
+			case 'DELEGATED-TO':
201
+			case 'MEMBER':
202
+			case 'SENT-BY':
203
+				return '"'.$value.'"';
204
+			break;
205
+
206
+			// Textual parameter types must be double quoted if they contain COLON, SEMICOLON
207
+			// or COMMA. Quoting always sounds easier and standards-conformant though.
208
+			case 'CN':
209
+				return '"'.$value.'"';
210
+			break;
211
+
212
+			// Parameters with enumerated legal values, just make them all caps
213
+			case 'CUTYPE':
214
+			case 'ENCODING':
215
+			case 'FBTYPE':
216
+			case 'FMTTYPE':
217
+			case 'LANGUAGE':
218
+			case 'PARTSTAT':
219
+			case 'RANGE':
220
+			case 'RELATED':
221
+			case 'RELTYPE':
222
+			case 'ROLE':
223
+			case 'RSVP':
224
+			case 'VALUE':
225
+				return strtoupper($value);
226
+			break;
227
+
228
+			// Parameters we shouldn't be messing with
229
+			case 'TZID':
230
+				return $value;
231
+			break;
232
+		}
233
+	}
234
+
235
+	function undo_value_formatting($parameter, $value) {
236
+	}
237 237
 }
238 238
 
239 239
 ?>
Please login to merge, or discard this patch.
phpicalendar/lib/bennu/iCalendar_rfc2445.php 1 patch
Indentation   +678 added lines, -678 removed lines patch added patch discarded remove patch
@@ -54,732 +54,732 @@
 block discarded – undo
54 54
 
55 55
 
56 56
 function rfc2445_fold($string) {
57
-    if(strlen($string) <= RFC2445_FOLDED_LINE_LENGTH) {
58
-        return $string;
59
-    }
57
+	if(strlen($string) <= RFC2445_FOLDED_LINE_LENGTH) {
58
+		return $string;
59
+	}
60 60
 
61
-    $retval = '';
61
+	$retval = '';
62 62
 
63
-    while(strlen($string) > RFC2445_FOLDED_LINE_LENGTH) {
64
-        $retval .= substr($string, 0, RFC2445_FOLDED_LINE_LENGTH - 1) . RFC2445_CRLF . ' ';
65
-        $string  = substr($string, RFC2445_FOLDED_LINE_LENGTH - 1);
66
-    }
63
+	while(strlen($string) > RFC2445_FOLDED_LINE_LENGTH) {
64
+		$retval .= substr($string, 0, RFC2445_FOLDED_LINE_LENGTH - 1) . RFC2445_CRLF . ' ';
65
+		$string  = substr($string, RFC2445_FOLDED_LINE_LENGTH - 1);
66
+	}
67 67
 
68
-    $retval .= $string;
68
+	$retval .= $string;
69 69
     
70
-    return $retval;
70
+	return $retval;
71 71
 
72 72
 }
73 73
 
74 74
 function rfc2445_unfold($string) {
75
-    for($i = 0; $i < strlen(RFC2445_WSP); ++$i) {
76
-        $string = str_replace(RFC2445_CRLF.substr(RFC2445_WSP, $i, 1), '', $string);
77
-    }
75
+	for($i = 0; $i < strlen(RFC2445_WSP); ++$i) {
76
+		$string = str_replace(RFC2445_CRLF.substr(RFC2445_WSP, $i, 1), '', $string);
77
+	}
78 78
 
79
-    return $string;
79
+	return $string;
80 80
 }
81 81
 
82 82
 function rfc2445_is_xname($name) {
83 83
 
84
-    // If it's less than 3 chars, it cannot be legal
85
-    if(strlen($name) < 3) {
86
-        return false;
87
-    }
84
+	// If it's less than 3 chars, it cannot be legal
85
+	if(strlen($name) < 3) {
86
+		return false;
87
+	}
88 88
 
89
-    // If it contains an illegal char anywhere, reject it
90
-    if(strspn($name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-') != strlen($name)) {
91
-        return false;
92
-    }
89
+	// If it contains an illegal char anywhere, reject it
90
+	if(strspn($name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-') != strlen($name)) {
91
+		return false;
92
+	}
93 93
 
94
-    // To be legal, it must still start with "X-"
95
-    return substr($name, 0, 2) === 'X-';
94
+	// To be legal, it must still start with "X-"
95
+	return substr($name, 0, 2) === 'X-';
96 96
 }
97 97
 
98 98
 function rfc2445_is_valid_value($value, $type) {
99 99
 
100
-    // This branch should only be taken with xname values
101
-    if($type === NULL) {
102
-        return true;
103
-    }
100
+	// This branch should only be taken with xname values
101
+	if($type === NULL) {
102
+		return true;
103
+	}
104 104
 
105
-    switch($type) {
106
-        case RFC2445_TYPE_CAL_ADDRESS:
107
-        case RFC2445_TYPE_URI:
108
-            if(!is_string($value)) {
109
-                return false;
110
-            }
105
+	switch($type) {
106
+		case RFC2445_TYPE_CAL_ADDRESS:
107
+		case RFC2445_TYPE_URI:
108
+			if(!is_string($value)) {
109
+				return false;
110
+			}
111 111
 
112
-            $valid_schemes = array('ftp', 'http', 'ldap', 'gopher', 'mailto', 'news', 'nntp', 'telnet', 'wais', 'file', 'prospero');
112
+			$valid_schemes = array('ftp', 'http', 'ldap', 'gopher', 'mailto', 'news', 'nntp', 'telnet', 'wais', 'file', 'prospero');
113 113
 
114
-            $pos = strpos($value, ':');
115
-            if(!$pos) {
116
-                return false;
117
-            }
114
+			$pos = strpos($value, ':');
115
+			if(!$pos) {
116
+				return false;
117
+			}
118 118
         
119
-            $scheme = strtolower(substr($value, 0, $pos));
120
-            $remain = substr($value, $pos + 1);
119
+			$scheme = strtolower(substr($value, 0, $pos));
120
+			$remain = substr($value, $pos + 1);
121 121
             
122
-            if(!in_array($scheme, $valid_schemes)) {
123
-                return false;
124
-            }
122
+			if(!in_array($scheme, $valid_schemes)) {
123
+				return false;
124
+			}
125 125
         
126
-            if($scheme === 'mailto') {
127
-                $regexp = '^[a-zA-Z0-9]+[_a-zA-Z0-9\-]*(\.[_a-z0-9\-]+)*@(([0-9a-zA-Z\-]+\.)+[a-zA-Z][0-9a-zA-Z\-]+|([0-9]{1,3}\.){3}[0-9]{1,3})$';
128
-            }
129
-            else {
130
-                $regexp = '^//(.+(:.*)?@)?(([0-9a-zA-Z\-]+\.)+[a-zA-Z][0-9a-zA-Z\-]+|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]{1,5})?(/.*)?$';
131
-            }
126
+			if($scheme === 'mailto') {
127
+				$regexp = '^[a-zA-Z0-9]+[_a-zA-Z0-9\-]*(\.[_a-z0-9\-]+)*@(([0-9a-zA-Z\-]+\.)+[a-zA-Z][0-9a-zA-Z\-]+|([0-9]{1,3}\.){3}[0-9]{1,3})$';
128
+			}
129
+			else {
130
+				$regexp = '^//(.+(:.*)?@)?(([0-9a-zA-Z\-]+\.)+[a-zA-Z][0-9a-zA-Z\-]+|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]{1,5})?(/.*)?$';
131
+			}
132 132
         
133
-            return ereg($regexp, $remain);
134
-        break;
133
+			return ereg($regexp, $remain);
134
+		break;
135 135
 
136
-        case RFC2445_TYPE_BINARY:
137
-            if(!is_string($value)) {
138
-                return false;
139
-            }
136
+		case RFC2445_TYPE_BINARY:
137
+			if(!is_string($value)) {
138
+				return false;
139
+			}
140 140
 
141
-            $len = strlen($value);
141
+			$len = strlen($value);
142 142
             
143
-            if($len % 4 != 0) {
144
-                return false;
145
-            }
146
-
147
-            for($i = 0; $i < $len; ++$i) {
148
-                $ch = $value{$i};
149
-                if(!($ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || $ch >= '0' && $ch <= '9' || $ch == '-' || $ch == '+')) {
150
-                    if($ch == '=' && $len - $i <= 2) {
151
-                        continue;
152
-                    }
153
-                    return false;
154
-                }
155
-            }
156
-            return true;
157
-        break;
158
-
159
-        case RFC2445_TYPE_BOOLEAN:
160
-            if(is_bool($value)) {
161
-                return true;
162
-            }
163
-            if(is_string($value)) {
164
-                $value = strtoupper($value);
165
-                return ($value == 'TRUE' || $value == 'FALSE');
166
-            }
167
-            return false;
168
-        break;
169
-
170
-        case RFC2445_TYPE_DATE:
171
-            if(is_int($value)) {
172
-                if($value < 0) {
173
-                    return false;
174
-                }
175
-                $value = "$value";
176
-            }
177
-            else if(!is_string($value)) {
178
-                return false;
179
-            }
180
-
181
-            if(strlen($value) != 8) {
182
-                return false;
183
-            }
184
-
185
-            $y = intval(substr($value, 0, 4));
186
-            $m = intval(substr($value, 4, 2));
187
-            $d = intval(substr($value, 6, 2));
188
-
189
-            return checkdate($m, $d, $y);
190
-        break;
191
-
192
-        case RFC2445_TYPE_DATE_TIME:
193
-            if(!is_string($value) || strlen($value) < 15) {
194
-                return false;
195
-            }
196
-
197
-            return($value{8} == 'T' && 
198
-                   rfc2445_is_valid_value(substr($value, 0, 8), RFC2445_TYPE_DATE) &&
199
-                   rfc2445_is_valid_value(substr($value, 9), RFC2445_TYPE_TIME));
200
-        break;
201
-
202
-        case RFC2445_TYPE_DURATION:
203
-            if(!is_string($value)) {
204
-                return false;
205
-            }
206
-
207
-            $len = strlen($value);
208
-
209
-            if($len < 3) {
210
-                // Minimum conformant length: "P1W"
211
-                return false;
212
-            }
213
-
214
-            if($value{0} == '+' || $value{0} == '-') {
215
-                $value = substr($value, 1);
216
-                --$len; // Don't forget to update this!
217
-            }
218
-
219
-            if($value{0} != 'P') {
220
-                return false;
221
-            }
222
-
223
-            // OK, now break it up
224
-            $num = '';
225
-            $allowed = 'WDT';
226
-
227
-            for($i = 1; $i < $len; ++$i) {
228
-                $ch = $value{$i};
229
-                if($ch >= '0' && $ch <= '9') {
230
-                    $num .= $ch;
231
-                    continue;
232
-                }
233
-                if(strpos($allowed, $ch) === false) {
234
-                    // Non-numeric character which shouldn't be here
235
-                    return false;
236
-                }
237
-                if($num === '' && $ch != 'T') {
238
-                    // Allowed non-numeric character, but no digits came before it
239
-                    return false;
240
-                }
241
-
242
-                // OK, $ch now holds a character which tells us what $num is
243
-                switch($ch) {
244
-                    case 'W':
245
-                        // If duration in weeks is specified, this must end the string
246
-                        return ($i == $len - 1);
247
-                    break;
248
-
249
-                    case 'D':
250
-                        // Days specified, now if anything comes after it must be a 'T'
251
-                        $allowed = 'T';
252
-                    break;
253
-
254
-                    case 'T':
255
-                        // Starting to specify time, H M S are now valid delimiters
256
-                        $allowed = 'HMS';
257
-                    break;
258
-
259
-                    case 'H':
260
-                        $allowed = 'M';
261
-                    break;
262
-
263
-                    case 'M':
264
-                        $allowed = 'S';
265
-                    break;
266
-
267
-                    case 'S':
268
-                        return ($i == $len - 1);
269
-                    break;
270
-                }
271
-
272
-                // If we 're going to continue, reset $num
273
-                $num = '';
274
-
275
-            }
276
-
277
-            // $num is kept for this reason: if we 're here, we ran out of chars
278
-            // therefore $num must be empty for the period to be legal
279
-            return ($num === '' && $ch != 'T');
280
-
281
-        break;
143
+			if($len % 4 != 0) {
144
+				return false;
145
+			}
146
+
147
+			for($i = 0; $i < $len; ++$i) {
148
+				$ch = $value{$i};
149
+				if(!($ch >= 'a' && $ch <= 'z' || $ch >= 'A' && $ch <= 'Z' || $ch >= '0' && $ch <= '9' || $ch == '-' || $ch == '+')) {
150
+					if($ch == '=' && $len - $i <= 2) {
151
+						continue;
152
+					}
153
+					return false;
154
+				}
155
+			}
156
+			return true;
157
+		break;
158
+
159
+		case RFC2445_TYPE_BOOLEAN:
160
+			if(is_bool($value)) {
161
+				return true;
162
+			}
163
+			if(is_string($value)) {
164
+				$value = strtoupper($value);
165
+				return ($value == 'TRUE' || $value == 'FALSE');
166
+			}
167
+			return false;
168
+		break;
169
+
170
+		case RFC2445_TYPE_DATE:
171
+			if(is_int($value)) {
172
+				if($value < 0) {
173
+					return false;
174
+				}
175
+				$value = "$value";
176
+			}
177
+			else if(!is_string($value)) {
178
+				return false;
179
+			}
180
+
181
+			if(strlen($value) != 8) {
182
+				return false;
183
+			}
184
+
185
+			$y = intval(substr($value, 0, 4));
186
+			$m = intval(substr($value, 4, 2));
187
+			$d = intval(substr($value, 6, 2));
188
+
189
+			return checkdate($m, $d, $y);
190
+		break;
191
+
192
+		case RFC2445_TYPE_DATE_TIME:
193
+			if(!is_string($value) || strlen($value) < 15) {
194
+				return false;
195
+			}
196
+
197
+			return($value{8} == 'T' && 
198
+				   rfc2445_is_valid_value(substr($value, 0, 8), RFC2445_TYPE_DATE) &&
199
+				   rfc2445_is_valid_value(substr($value, 9), RFC2445_TYPE_TIME));
200
+		break;
201
+
202
+		case RFC2445_TYPE_DURATION:
203
+			if(!is_string($value)) {
204
+				return false;
205
+			}
206
+
207
+			$len = strlen($value);
208
+
209
+			if($len < 3) {
210
+				// Minimum conformant length: "P1W"
211
+				return false;
212
+			}
213
+
214
+			if($value{0} == '+' || $value{0} == '-') {
215
+				$value = substr($value, 1);
216
+				--$len; // Don't forget to update this!
217
+			}
218
+
219
+			if($value{0} != 'P') {
220
+				return false;
221
+			}
222
+
223
+			// OK, now break it up
224
+			$num = '';
225
+			$allowed = 'WDT';
226
+
227
+			for($i = 1; $i < $len; ++$i) {
228
+				$ch = $value{$i};
229
+				if($ch >= '0' && $ch <= '9') {
230
+					$num .= $ch;
231
+					continue;
232
+				}
233
+				if(strpos($allowed, $ch) === false) {
234
+					// Non-numeric character which shouldn't be here
235
+					return false;
236
+				}
237
+				if($num === '' && $ch != 'T') {
238
+					// Allowed non-numeric character, but no digits came before it
239
+					return false;
240
+				}
241
+
242
+				// OK, $ch now holds a character which tells us what $num is
243
+				switch($ch) {
244
+					case 'W':
245
+						// If duration in weeks is specified, this must end the string
246
+						return ($i == $len - 1);
247
+					break;
248
+
249
+					case 'D':
250
+						// Days specified, now if anything comes after it must be a 'T'
251
+						$allowed = 'T';
252
+					break;
253
+
254
+					case 'T':
255
+						// Starting to specify time, H M S are now valid delimiters
256
+						$allowed = 'HMS';
257
+					break;
258
+
259
+					case 'H':
260
+						$allowed = 'M';
261
+					break;
262
+
263
+					case 'M':
264
+						$allowed = 'S';
265
+					break;
266
+
267
+					case 'S':
268
+						return ($i == $len - 1);
269
+					break;
270
+				}
271
+
272
+				// If we 're going to continue, reset $num
273
+				$num = '';
274
+
275
+			}
276
+
277
+			// $num is kept for this reason: if we 're here, we ran out of chars
278
+			// therefore $num must be empty for the period to be legal
279
+			return ($num === '' && $ch != 'T');
280
+
281
+		break;
282 282
         
283
-        case RFC2445_TYPE_FLOAT:
284
-            if(is_float($value)) {
285
-                return true;
286
-            }
287
-            if(!is_string($value) || $value === '') {
288
-                return false;
289
-            }
290
-
291
-            $dot = false;
292
-            $int = false;
293
-            $len = strlen($value);
294
-            for($i = 0; $i < $len; ++$i) {
295
-                switch($value{$i}) {
296
-                    case '-': case '+':
297
-                        // A sign can only be seen at position 0 and cannot be the only char
298
-                        if($i != 0 || $len == 1) {
299
-                            return false;
300
-                        }
301
-                    break;
302
-                    case '.':
303
-                        // A second dot is an error
304
-                        // Make sure we had at least one int before the dot
305
-                        if($dot || !$int) {
306
-                            return false;
307
-                        }
308
-                        $dot = true;
309
-                        // Make also sure that the float doesn't end with a dot
310
-                        if($i == $len - 1) {
311
-                            return false;
312
-                        }
313
-                    break;
314
-                    case '0': case '1': case '2': case '3': case '4':
315
-                    case '5': case '6': case '7': case '8': case '9':
316
-                        $int = true;
317
-                    break;
318
-                    default:
319
-                        // Any other char is a no-no
320
-                        return false;
321
-                    break;
322
-                }
323
-            }
324
-            return true;
325
-        break;
326
-
327
-        case RFC2445_TYPE_INTEGER:
328
-            if(is_int($value)) {
329
-                return true;
330
-            }
331
-            if(!is_string($value) || $value === '') {
332
-                return false;
333
-            }
334
-
335
-            if($value{0} == '+' || $value{0} == '-') {
336
-                if(strlen($value) == 1) {
337
-                    return false;
338
-                }
339
-                $value = substr($value, 1);
340
-            }
341
-
342
-            if(strspn($value, '0123456789') != strlen($value)) {
343
-                return false;
344
-            }
345
-
346
-            return ($value >= -2147483648 && $value <= 2147483647);
347
-        break;
348
-
349
-        case RFC2445_TYPE_PERIOD:
350
-            if(!is_string($value) || empty($value)) {
351
-                return false;
352
-            }
353
-
354
-            $parts = explode('/', $value);
355
-            if(count($parts) != 2) {
356
-                return false;
357
-            }
358
-
359
-            if(!rfc2445_is_valid_value($parts[0], RFC2445_TYPE_DATE_TIME)) {
360
-                return false;
361
-            }
362
-
363
-            // Two legal cases for the second part:
364
-            if(rfc2445_is_valid_value($parts[1], RFC2445_TYPE_DATE_TIME)) {
365
-                // It has to be after the start time, so
366
-                return ($parts[1] > $parts[0]);
367
-            }
368
-            else if(rfc2445_is_valid_value($parts[1], RFC2445_TYPE_DURATION)) {
369
-                // The period MUST NOT be negative
370
-                return ($parts[1]{0} != '-');
371
-            }
372
-
373
-            // It seems to be illegal
374
-            return false;
375
-        break;
376
-
377
-        case RFC2445_TYPE_RECUR:
378
-            if(!is_string($value)) {
379
-                return false;
380
-            }
381
-
382
-            $parts = explode(';', strtoupper($value));
383
-
384
-            // First of all, we need at least a FREQ and a UNTIL or COUNT part, so...
385
-            if(count($parts) < 2) {
386
-                return false;
387
-            }
388
-
389
-            // Let's get that into a more easily comprehensible format
390
-            $vars = array();
391
-            foreach($parts as $part) {
392
-
393
-                $pieces = explode('=', $part);
394
-                // There must be exactly 2 pieces, e.g. FREQ=WEEKLY
395
-                if(count($pieces) != 2) {
396
-                    return false;
397
-                }
398
-
399
-                // It's illegal for a variable to appear twice
400
-                if(isset($vars[$pieces[0]])) {
401
-                    return false;
402
-                }
403
-
404
-                // Sounds good
405
-                $vars[$pieces[0]] = $pieces[1];
406
-            }
407
-
408
-            // OK... now to test everything else
409
-
410
-            // FREQ must be the first thing appearing
411
-            reset($vars);
412
-            if(key($vars) != 'FREQ') {
413
-                return false;
414
-            }
415
-
416
-            // It's illegal to have both UNTIL and COUNT appear
417
-            if(isset($vars['UNTIL']) && isset($vars['COUNT'])) {
418
-                return false;
419
-            }
420
-
421
-            // Special case: BYWEEKNO is only valid for FREQ=YEARLY
422
-            if(isset($vars['BYWEEKNO']) && $vars['FREQ'] != 'YEARLY') {
423
-                return false;
424
-            }
425
-
426
-            // Special case: BYSETPOS is only valid if another BY option is specified
427
-            if(isset($vars['BYSETPOS'])) {
428
-                $options = array('BYSECOND', 'BYMINUTE', 'BYHOUR', 'BYDAY', 'BYMONTHDAY', 'BYYEARDAY', 'BYWEEKNO', 'BYMONTH');
429
-                $defined = array_keys($vars);
430
-                $common  = array_intersect($options, $defined);
431
-                if(empty($common)) {
432
-                    return false;
433
-                }
434
-            }
435
-
436
-            // OK, now simply check if each element has a valid value,
437
-            // unsetting them on the way. If at the end the array still
438
-            // has some elements, they are illegal.
439
-
440
-            if($vars['FREQ'] != 'SECONDLY' && $vars['FREQ'] != 'MINUTELY' && $vars['FREQ'] != 'HOURLY' && 
441
-               $vars['FREQ'] != 'DAILY'    && $vars['FREQ'] != 'WEEKLY' &&
442
-               $vars['FREQ'] != 'MONTHLY'  && $vars['FREQ'] != 'YEARLY') {
443
-                return false;
444
-            }
445
-            unset($vars['FREQ']);
446
-
447
-            // Set this, we may need it later
448
-            $weekdays = explode(',', RFC2445_WEEKDAYS);
449
-
450
-            if(isset($vars['UNTIL'])) {
451
-                if(rfc2445_is_valid_value($vars['UNTIL'], RFC2445_TYPE_DATE_TIME)) {
452
-                    // The time MUST be in UTC format
453
-                    if(!(substr($vars['UNTIL'], -1) == 'Z')) {
454
-                        return false;
455
-                    }
456
-                }
457
-                else if(!rfc2445_is_valid_value($vars['UNTIL'], RFC2445_TYPE_DATE_TIME)) {
458
-                    return false;
459
-                }
460
-            }
461
-            unset($vars['UNTIL']);
462
-
463
-
464
-            if(isset($vars['COUNT'])) {
465
-                if(empty($vars['COUNT'])) {
466
-                    // This also catches the string '0', which makes no sense
467
-                    return false;
468
-                }
469
-                if(strspn($vars['COUNT'], '0123456789') != strlen($vars['COUNT'])) {
470
-                    return false;
471
-                }
472
-            }
473
-            unset($vars['COUNT']);
283
+		case RFC2445_TYPE_FLOAT:
284
+			if(is_float($value)) {
285
+				return true;
286
+			}
287
+			if(!is_string($value) || $value === '') {
288
+				return false;
289
+			}
290
+
291
+			$dot = false;
292
+			$int = false;
293
+			$len = strlen($value);
294
+			for($i = 0; $i < $len; ++$i) {
295
+				switch($value{$i}) {
296
+					case '-': case '+':
297
+						// A sign can only be seen at position 0 and cannot be the only char
298
+						if($i != 0 || $len == 1) {
299
+							return false;
300
+						}
301
+					break;
302
+					case '.':
303
+						// A second dot is an error
304
+						// Make sure we had at least one int before the dot
305
+						if($dot || !$int) {
306
+							return false;
307
+						}
308
+						$dot = true;
309
+						// Make also sure that the float doesn't end with a dot
310
+						if($i == $len - 1) {
311
+							return false;
312
+						}
313
+					break;
314
+					case '0': case '1': case '2': case '3': case '4':
315
+					case '5': case '6': case '7': case '8': case '9':
316
+						$int = true;
317
+					break;
318
+					default:
319
+						// Any other char is a no-no
320
+						return false;
321
+					break;
322
+				}
323
+			}
324
+			return true;
325
+		break;
326
+
327
+		case RFC2445_TYPE_INTEGER:
328
+			if(is_int($value)) {
329
+				return true;
330
+			}
331
+			if(!is_string($value) || $value === '') {
332
+				return false;
333
+			}
334
+
335
+			if($value{0} == '+' || $value{0} == '-') {
336
+				if(strlen($value) == 1) {
337
+					return false;
338
+				}
339
+				$value = substr($value, 1);
340
+			}
341
+
342
+			if(strspn($value, '0123456789') != strlen($value)) {
343
+				return false;
344
+			}
345
+
346
+			return ($value >= -2147483648 && $value <= 2147483647);
347
+		break;
348
+
349
+		case RFC2445_TYPE_PERIOD:
350
+			if(!is_string($value) || empty($value)) {
351
+				return false;
352
+			}
353
+
354
+			$parts = explode('/', $value);
355
+			if(count($parts) != 2) {
356
+				return false;
357
+			}
358
+
359
+			if(!rfc2445_is_valid_value($parts[0], RFC2445_TYPE_DATE_TIME)) {
360
+				return false;
361
+			}
362
+
363
+			// Two legal cases for the second part:
364
+			if(rfc2445_is_valid_value($parts[1], RFC2445_TYPE_DATE_TIME)) {
365
+				// It has to be after the start time, so
366
+				return ($parts[1] > $parts[0]);
367
+			}
368
+			else if(rfc2445_is_valid_value($parts[1], RFC2445_TYPE_DURATION)) {
369
+				// The period MUST NOT be negative
370
+				return ($parts[1]{0} != '-');
371
+			}
372
+
373
+			// It seems to be illegal
374
+			return false;
375
+		break;
376
+
377
+		case RFC2445_TYPE_RECUR:
378
+			if(!is_string($value)) {
379
+				return false;
380
+			}
381
+
382
+			$parts = explode(';', strtoupper($value));
383
+
384
+			// First of all, we need at least a FREQ and a UNTIL or COUNT part, so...
385
+			if(count($parts) < 2) {
386
+				return false;
387
+			}
388
+
389
+			// Let's get that into a more easily comprehensible format
390
+			$vars = array();
391
+			foreach($parts as $part) {
392
+
393
+				$pieces = explode('=', $part);
394
+				// There must be exactly 2 pieces, e.g. FREQ=WEEKLY
395
+				if(count($pieces) != 2) {
396
+					return false;
397
+				}
398
+
399
+				// It's illegal for a variable to appear twice
400
+				if(isset($vars[$pieces[0]])) {
401
+					return false;
402
+				}
403
+
404
+				// Sounds good
405
+				$vars[$pieces[0]] = $pieces[1];
406
+			}
407
+
408
+			// OK... now to test everything else
409
+
410
+			// FREQ must be the first thing appearing
411
+			reset($vars);
412
+			if(key($vars) != 'FREQ') {
413
+				return false;
414
+			}
415
+
416
+			// It's illegal to have both UNTIL and COUNT appear
417
+			if(isset($vars['UNTIL']) && isset($vars['COUNT'])) {
418
+				return false;
419
+			}
420
+
421
+			// Special case: BYWEEKNO is only valid for FREQ=YEARLY
422
+			if(isset($vars['BYWEEKNO']) && $vars['FREQ'] != 'YEARLY') {
423
+				return false;
424
+			}
425
+
426
+			// Special case: BYSETPOS is only valid if another BY option is specified
427
+			if(isset($vars['BYSETPOS'])) {
428
+				$options = array('BYSECOND', 'BYMINUTE', 'BYHOUR', 'BYDAY', 'BYMONTHDAY', 'BYYEARDAY', 'BYWEEKNO', 'BYMONTH');
429
+				$defined = array_keys($vars);
430
+				$common  = array_intersect($options, $defined);
431
+				if(empty($common)) {
432
+					return false;
433
+				}
434
+			}
435
+
436
+			// OK, now simply check if each element has a valid value,
437
+			// unsetting them on the way. If at the end the array still
438
+			// has some elements, they are illegal.
439
+
440
+			if($vars['FREQ'] != 'SECONDLY' && $vars['FREQ'] != 'MINUTELY' && $vars['FREQ'] != 'HOURLY' && 
441
+			   $vars['FREQ'] != 'DAILY'    && $vars['FREQ'] != 'WEEKLY' &&
442
+			   $vars['FREQ'] != 'MONTHLY'  && $vars['FREQ'] != 'YEARLY') {
443
+				return false;
444
+			}
445
+			unset($vars['FREQ']);
446
+
447
+			// Set this, we may need it later
448
+			$weekdays = explode(',', RFC2445_WEEKDAYS);
449
+
450
+			if(isset($vars['UNTIL'])) {
451
+				if(rfc2445_is_valid_value($vars['UNTIL'], RFC2445_TYPE_DATE_TIME)) {
452
+					// The time MUST be in UTC format
453
+					if(!(substr($vars['UNTIL'], -1) == 'Z')) {
454
+						return false;
455
+					}
456
+				}
457
+				else if(!rfc2445_is_valid_value($vars['UNTIL'], RFC2445_TYPE_DATE_TIME)) {
458
+					return false;
459
+				}
460
+			}
461
+			unset($vars['UNTIL']);
462
+
463
+
464
+			if(isset($vars['COUNT'])) {
465
+				if(empty($vars['COUNT'])) {
466
+					// This also catches the string '0', which makes no sense
467
+					return false;
468
+				}
469
+				if(strspn($vars['COUNT'], '0123456789') != strlen($vars['COUNT'])) {
470
+					return false;
471
+				}
472
+			}
473
+			unset($vars['COUNT']);
474 474
 
475 475
             
476
-            if(isset($vars['INTERVAL'])) {
477
-                if(empty($vars['INTERVAL'])) {
478
-                    // This also catches the string '0', which makes no sense
479
-                    return false;
480
-                }
481
-                if(strspn($vars['INTERVAL'], '0123456789') != strlen($vars['INTERVAL'])) {
482
-                    return false;
483
-                }
484
-            }
485
-            unset($vars['INTERVAL']);
476
+			if(isset($vars['INTERVAL'])) {
477
+				if(empty($vars['INTERVAL'])) {
478
+					// This also catches the string '0', which makes no sense
479
+					return false;
480
+				}
481
+				if(strspn($vars['INTERVAL'], '0123456789') != strlen($vars['INTERVAL'])) {
482
+					return false;
483
+				}
484
+			}
485
+			unset($vars['INTERVAL']);
486 486
 
487 487
             
488
-            if(isset($vars['BYSECOND'])) {
489
-                if($vars['BYSECOND'] == '') {
490
-                    return false;
491
-                }
492
-                // Comma also allowed
493
-                if(strspn($vars['BYSECOND'], '0123456789,') != strlen($vars['BYSECOND'])) {
494
-                    return false;
495
-                }
496
-                $secs = explode(',', $vars['BYSECOND']);
497
-                foreach($secs as $sec) {
498
-                    if($sec == '' || $sec < 0 || $sec > 59) {
499
-                        return false;
500
-                    }
501
-                }
502
-            }
503
-            unset($vars['BYSECOND']);
488
+			if(isset($vars['BYSECOND'])) {
489
+				if($vars['BYSECOND'] == '') {
490
+					return false;
491
+				}
492
+				// Comma also allowed
493
+				if(strspn($vars['BYSECOND'], '0123456789,') != strlen($vars['BYSECOND'])) {
494
+					return false;
495
+				}
496
+				$secs = explode(',', $vars['BYSECOND']);
497
+				foreach($secs as $sec) {
498
+					if($sec == '' || $sec < 0 || $sec > 59) {
499
+						return false;
500
+					}
501
+				}
502
+			}
503
+			unset($vars['BYSECOND']);
504 504
 
505 505
             
506
-            if(isset($vars['BYMINUTE'])) {
507
-                if($vars['BYMINUTE'] == '') {
508
-                    return false;
509
-                }
510
-                // Comma also allowed
511
-                if(strspn($vars['BYMINUTE'], '0123456789,') != strlen($vars['BYMINUTE'])) {
512
-                    return false;
513
-                }
514
-                $mins = explode(',', $vars['BYMINUTE']);
515
-                foreach($mins as $min) {
516
-                    if($min == '' || $min < 0 || $min > 59) {
517
-                        return false;
518
-                    }
519
-                }
520
-            }
521
-            unset($vars['BYMINUTE']);
506
+			if(isset($vars['BYMINUTE'])) {
507
+				if($vars['BYMINUTE'] == '') {
508
+					return false;
509
+				}
510
+				// Comma also allowed
511
+				if(strspn($vars['BYMINUTE'], '0123456789,') != strlen($vars['BYMINUTE'])) {
512
+					return false;
513
+				}
514
+				$mins = explode(',', $vars['BYMINUTE']);
515
+				foreach($mins as $min) {
516
+					if($min == '' || $min < 0 || $min > 59) {
517
+						return false;
518
+					}
519
+				}
520
+			}
521
+			unset($vars['BYMINUTE']);
522 522
 
523 523
             
524
-            if(isset($vars['BYHOUR'])) {
525
-                if($vars['BYHOUR'] == '') {
526
-                    return false;
527
-                }
528
-                // Comma also allowed
529
-                if(strspn($vars['BYHOUR'], '0123456789,') != strlen($vars['BYHOUR'])) {
530
-                    return false;
531
-                }
532
-                $hours = explode(',', $vars['BYHOUR']);
533
-                foreach($hours as $hour) {
534
-                    if($hour == '' || $hour < 0 || $hour > 23) {
535
-                        return false;
536
-                    }
537
-                }
538
-            }
539
-            unset($vars['BYHOUR']);
524
+			if(isset($vars['BYHOUR'])) {
525
+				if($vars['BYHOUR'] == '') {
526
+					return false;
527
+				}
528
+				// Comma also allowed
529
+				if(strspn($vars['BYHOUR'], '0123456789,') != strlen($vars['BYHOUR'])) {
530
+					return false;
531
+				}
532
+				$hours = explode(',', $vars['BYHOUR']);
533
+				foreach($hours as $hour) {
534
+					if($hour == '' || $hour < 0 || $hour > 23) {
535
+						return false;
536
+					}
537
+				}
538
+			}
539
+			unset($vars['BYHOUR']);
540 540
             
541 541
 
542
-            if(isset($vars['BYDAY'])) {
543
-                if(empty($vars['BYDAY'])) {
544
-                    return false;
545
-                }
542
+			if(isset($vars['BYDAY'])) {
543
+				if(empty($vars['BYDAY'])) {
544
+					return false;
545
+				}
546 546
 
547
-                // First off, split up all values we may have
548
-                $days = explode(',', $vars['BYDAY']);
547
+				// First off, split up all values we may have
548
+				$days = explode(',', $vars['BYDAY']);
549 549
                 
550
-                foreach($days as $day) {
551
-                    $daypart = substr($day, -2);
552
-                    if(!in_array($daypart, $weekdays)) {
553
-                        return false;
554
-                    }
555
-
556
-                    if(strlen($day) > 2) {
557
-                        $intpart = substr($day, 0, strlen($day) - 2);
558
-                        if(!rfc2445_is_valid_value($intpart, RFC2445_TYPE_INTEGER)) {
559
-                            return false;
560
-                        }
561
-                        if(intval($intpart) == 0) {
562
-                            return false;
563
-                        }
564
-                    }
565
-                }
566
-            }
567
-            unset($vars['BYDAY']);
568
-
569
-
570
-            if(isset($vars['BYMONTHDAY'])) {
571
-                if(empty($vars['BYMONTHDAY'])) {
572
-                    return false;
573
-                }
574
-                $mdays = explode(',', $vars['BYMONTHDAY']);
575
-                foreach($mdays as $mday) {
576
-                    if(!rfc2445_is_valid_value($mday, RFC2445_TYPE_INTEGER)) {
577
-                        return false;
578
-                    }
579
-                    $mday = abs(intval($mday));
580
-                    if($mday == 0 || $mday > 31) {
581
-                        return false;
582
-                    }
583
-                }
584
-            }
585
-            unset($vars['BYMONTHDAY']);
586
-
587
-
588
-            if(isset($vars['BYYEARDAY'])) {
589
-                if(empty($vars['BYYEARDAY'])) {
590
-                    return false;
591
-                }
592
-                $ydays = explode(',', $vars['BYYEARDAY']);
593
-                foreach($ydays as $yday) {
594
-                    if(!rfc2445_is_valid_value($yday, RFC2445_TYPE_INTEGER)) {
595
-                        return false;
596
-                    }
597
-                    $yday = abs(intval($yday));
598
-                    if($yday == 0 || $yday > 366) {
599
-                        return false;
600
-                    }
601
-                }
602
-            }
603
-            unset($vars['BYYEARDAY']);
604
-
605
-
606
-            if(isset($vars['BYWEEKNO'])) {
607
-                if(empty($vars['BYWEEKNO'])) {
608
-                    return false;
609
-                }
610
-                $weeknos = explode(',', $vars['BYWEEKNO']);
611
-                foreach($weeknos as $weekno) {
612
-                    if(!rfc2445_is_valid_value($weekno, RFC2445_TYPE_INTEGER)) {
613
-                        return false;
614
-                    }
615
-                    $weekno = abs(intval($weekno));
616
-                    if($weekno == 0 || $weekno > 53) {
617
-                        return false;
618
-                    }
619
-                }
620
-            }
621
-            unset($vars['BYWEEKNO']);
622
-
623
-
624
-            if(isset($vars['BYMONTH'])) {
625
-                if(empty($vars['BYMONTH'])) {
626
-                    return false;
627
-                }
628
-                // Comma also allowed
629
-                if(strspn($vars['BYMONTH'], '0123456789,') != strlen($vars['BYMONTH'])) {
630
-                    return false;
631
-                }
632
-                $months = explode(',', $vars['BYMONTH']);
633
-                foreach($months as $month) {
634
-                    if($month == '' || $month < 1 || $month > 12) {
635
-                        return false;
636
-                    }
637
-                }
638
-            }
639
-            unset($vars['BYMONTH']);
640
-
641
-
642
-            if(isset($vars['BYSETPOS'])) {
643
-                if(empty($vars['BYSETPOS'])) {
644
-                    return false;
645
-                }
646
-                $sets = explode(',', $vars['BYSETPOS']);
647
-                foreach($sets as $set) {
648
-                    if(!rfc2445_is_valid_value($set, RFC2445_TYPE_INTEGER)) {
649
-                        return false;
650
-                    }
651
-                    $set = abs(intval($set));
652
-                    if($set == 0 || $set > 366) {
653
-                        return false;
654
-                    }
655
-                }
656
-            }
657
-            unset($vars['BYSETPOS']);
658
-
659
-
660
-            if(isset($vars['WKST'])) {
661
-                if(!in_array($vars['WKST'], $weekdays)) {
662
-                    return false;
663
-                }
664
-            }
665
-            unset($vars['WKST']);
666
-
667
-
668
-            // Any remaining vars must be x-names
669
-            if(empty($vars)) {
670
-                return true;
671
-            }
672
-
673
-            foreach($vars as $name => $var) {
674
-                if(!rfc2445_is_xname($name)) {
675
-                    return false;
676
-                }
677
-            }
678
-
679
-            // At last, all is OK!
680
-            return true;
681
-
682
-        break;
683
-
684
-        case RFC2445_TYPE_TEXT:
685
-            return true;
686
-        break;
687
-
688
-        case RFC2445_TYPE_TIME:
689
-            if(is_int($value)) {
690
-                if($value < 0) {
691
-                    return false;
692
-                }
693
-                $value = "$value";
694
-            }
695
-            else if(!is_string($value)) {
696
-                return false;
697
-            }
698
-
699
-            if(strlen($value) == 7) {
700
-                if(strtoupper(substr($value, -1)) != 'Z') {
701
-                    return false;
702
-                }
703
-                $value = substr($value, 0, 6);
704
-            }
705
-            if(strlen($value) != 6) {
706
-                return false;
707
-            }
708
-
709
-            $h = intval(substr($value, 0, 2));
710
-            $m = intval(substr($value, 2, 2));
711
-            $s = intval(substr($value, 4, 2));
712
-
713
-            return ($h <= 23 && $m <= 59 && $s <= 60);
714
-        break;
715
-
716
-        case RFC2445_TYPE_UTC_OFFSET:
717
-            if(is_int($value)) {
718
-                if($value >= 0) {
719
-                    $value = "+$value";
720
-                }
721
-                else {
722
-                    $value = "$value";
723
-                }
724
-            }
725
-            else if(!is_string($value)) {
726
-                return false;
727
-            }
728
-
729
-            if(strlen($value) == 7) {
730
-                $s = intval(substr($value, 5, 2));
731
-                $value = substr($value, 0, 5);
732
-            }
733
-            if(strlen($value) != 5 || $value == "-0000") {
734
-                return false;
735
-            }
736
-
737
-            if($value{0} != '+' && $value{0} != '-') {
738
-                return false;
739
-            }
740
-
741
-            $h = intval(substr($value, 1, 2));
742
-            $m = intval(substr($value, 3, 2));
743
-
744
-            return ($h <= 23 && $m <= 59 && $s <= 59);
745
-        break;
746
-    }
747
-
748
-    // TODO: remove this assertion
749
-    trigger_error('bad code path', E_USER_WARNING);
750
-    var_dump($type);
751
-    return false;
550
+				foreach($days as $day) {
551
+					$daypart = substr($day, -2);
552
+					if(!in_array($daypart, $weekdays)) {
553
+						return false;
554
+					}
555
+
556
+					if(strlen($day) > 2) {
557
+						$intpart = substr($day, 0, strlen($day) - 2);
558
+						if(!rfc2445_is_valid_value($intpart, RFC2445_TYPE_INTEGER)) {
559
+							return false;
560
+						}
561
+						if(intval($intpart) == 0) {
562
+							return false;
563
+						}
564
+					}
565
+				}
566
+			}
567
+			unset($vars['BYDAY']);
568
+
569
+
570
+			if(isset($vars['BYMONTHDAY'])) {
571
+				if(empty($vars['BYMONTHDAY'])) {
572
+					return false;
573
+				}
574
+				$mdays = explode(',', $vars['BYMONTHDAY']);
575
+				foreach($mdays as $mday) {
576
+					if(!rfc2445_is_valid_value($mday, RFC2445_TYPE_INTEGER)) {
577
+						return false;
578
+					}
579
+					$mday = abs(intval($mday));
580
+					if($mday == 0 || $mday > 31) {
581
+						return false;
582
+					}
583
+				}
584
+			}
585
+			unset($vars['BYMONTHDAY']);
586
+
587
+
588
+			if(isset($vars['BYYEARDAY'])) {
589
+				if(empty($vars['BYYEARDAY'])) {
590
+					return false;
591
+				}
592
+				$ydays = explode(',', $vars['BYYEARDAY']);
593
+				foreach($ydays as $yday) {
594
+					if(!rfc2445_is_valid_value($yday, RFC2445_TYPE_INTEGER)) {
595
+						return false;
596
+					}
597
+					$yday = abs(intval($yday));
598
+					if($yday == 0 || $yday > 366) {
599
+						return false;
600
+					}
601
+				}
602
+			}
603
+			unset($vars['BYYEARDAY']);
604
+
605
+
606
+			if(isset($vars['BYWEEKNO'])) {
607
+				if(empty($vars['BYWEEKNO'])) {
608
+					return false;
609
+				}
610
+				$weeknos = explode(',', $vars['BYWEEKNO']);
611
+				foreach($weeknos as $weekno) {
612
+					if(!rfc2445_is_valid_value($weekno, RFC2445_TYPE_INTEGER)) {
613
+						return false;
614
+					}
615
+					$weekno = abs(intval($weekno));
616
+					if($weekno == 0 || $weekno > 53) {
617
+						return false;
618
+					}
619
+				}
620
+			}
621
+			unset($vars['BYWEEKNO']);
622
+
623
+
624
+			if(isset($vars['BYMONTH'])) {
625
+				if(empty($vars['BYMONTH'])) {
626
+					return false;
627
+				}
628
+				// Comma also allowed
629
+				if(strspn($vars['BYMONTH'], '0123456789,') != strlen($vars['BYMONTH'])) {
630
+					return false;
631
+				}
632
+				$months = explode(',', $vars['BYMONTH']);
633
+				foreach($months as $month) {
634
+					if($month == '' || $month < 1 || $month > 12) {
635
+						return false;
636
+					}
637
+				}
638
+			}
639
+			unset($vars['BYMONTH']);
640
+
641
+
642
+			if(isset($vars['BYSETPOS'])) {
643
+				if(empty($vars['BYSETPOS'])) {
644
+					return false;
645
+				}
646
+				$sets = explode(',', $vars['BYSETPOS']);
647
+				foreach($sets as $set) {
648
+					if(!rfc2445_is_valid_value($set, RFC2445_TYPE_INTEGER)) {
649
+						return false;
650
+					}
651
+					$set = abs(intval($set));
652
+					if($set == 0 || $set > 366) {
653
+						return false;
654
+					}
655
+				}
656
+			}
657
+			unset($vars['BYSETPOS']);
658
+
659
+
660
+			if(isset($vars['WKST'])) {
661
+				if(!in_array($vars['WKST'], $weekdays)) {
662
+					return false;
663
+				}
664
+			}
665
+			unset($vars['WKST']);
666
+
667
+
668
+			// Any remaining vars must be x-names
669
+			if(empty($vars)) {
670
+				return true;
671
+			}
672
+
673
+			foreach($vars as $name => $var) {
674
+				if(!rfc2445_is_xname($name)) {
675
+					return false;
676
+				}
677
+			}
678
+
679
+			// At last, all is OK!
680
+			return true;
681
+
682
+		break;
683
+
684
+		case RFC2445_TYPE_TEXT:
685
+			return true;
686
+		break;
687
+
688
+		case RFC2445_TYPE_TIME:
689
+			if(is_int($value)) {
690
+				if($value < 0) {
691
+					return false;
692
+				}
693
+				$value = "$value";
694
+			}
695
+			else if(!is_string($value)) {
696
+				return false;
697
+			}
698
+
699
+			if(strlen($value) == 7) {
700
+				if(strtoupper(substr($value, -1)) != 'Z') {
701
+					return false;
702
+				}
703
+				$value = substr($value, 0, 6);
704
+			}
705
+			if(strlen($value) != 6) {
706
+				return false;
707
+			}
708
+
709
+			$h = intval(substr($value, 0, 2));
710
+			$m = intval(substr($value, 2, 2));
711
+			$s = intval(substr($value, 4, 2));
712
+
713
+			return ($h <= 23 && $m <= 59 && $s <= 60);
714
+		break;
715
+
716
+		case RFC2445_TYPE_UTC_OFFSET:
717
+			if(is_int($value)) {
718
+				if($value >= 0) {
719
+					$value = "+$value";
720
+				}
721
+				else {
722
+					$value = "$value";
723
+				}
724
+			}
725
+			else if(!is_string($value)) {
726
+				return false;
727
+			}
728
+
729
+			if(strlen($value) == 7) {
730
+				$s = intval(substr($value, 5, 2));
731
+				$value = substr($value, 0, 5);
732
+			}
733
+			if(strlen($value) != 5 || $value == "-0000") {
734
+				return false;
735
+			}
736
+
737
+			if($value{0} != '+' && $value{0} != '-') {
738
+				return false;
739
+			}
740
+
741
+			$h = intval(substr($value, 1, 2));
742
+			$m = intval(substr($value, 3, 2));
743
+
744
+			return ($h <= 23 && $m <= 59 && $s <= 59);
745
+		break;
746
+	}
747
+
748
+	// TODO: remove this assertion
749
+	trigger_error('bad code path', E_USER_WARNING);
750
+	var_dump($type);
751
+	return false;
752 752
 }
753 753
 
754 754
 function rfc2445_do_value_formatting($value, $type) {
755
-    // Note: this does not only do formatting; it also does conversion to string!
756
-    switch($type) {
757
-        case RFC2445_TYPE_CAL_ADDRESS:
758
-        case RFC2445_TYPE_URI:
759
-            // Enclose in double quotes
760
-            $value = '"'.$value.'"';
761
-        break;
762
-        case RFC2445_TYPE_TEXT:
763
-            // Escape entities
764
-            $value = strtr($value, array("\n" => '\\n', '\\' => '\\\\', ',' => '\\,', ';' => '\\;'));
765
-        break;
766
-    }
767
-    return $value;
755
+	// Note: this does not only do formatting; it also does conversion to string!
756
+	switch($type) {
757
+		case RFC2445_TYPE_CAL_ADDRESS:
758
+		case RFC2445_TYPE_URI:
759
+			// Enclose in double quotes
760
+			$value = '"'.$value.'"';
761
+		break;
762
+		case RFC2445_TYPE_TEXT:
763
+			// Escape entities
764
+			$value = strtr($value, array("\n" => '\\n', '\\' => '\\\\', ',' => '\\,', ';' => '\\;'));
765
+		break;
766
+	}
767
+	return $value;
768 768
 }
769 769
 
770 770
 function rfc2445_undo_value_formatting($value, $type) {
771
-    switch($type) {
772
-        case RFC2445_TYPE_CAL_ADDRESS:
773
-        case RFC2445_TYPE_URI:
774
-            // Trim beginning and end double quote
775
-            $value = substr($value, 1, strlen($value) - 2);
776
-        break;
777
-        case RFC2445_TYPE_TEXT:
778
-            // Unescape entities
779
-            $value = strtr($value, array('\\n' => "\n", '\\N' => "\n", '\\\\' => '\\', '\\,' => ',', '\\;' => ';'));
780
-        break;
781
-    }
782
-    return $value;
771
+	switch($type) {
772
+		case RFC2445_TYPE_CAL_ADDRESS:
773
+		case RFC2445_TYPE_URI:
774
+			// Trim beginning and end double quote
775
+			$value = substr($value, 1, strlen($value) - 2);
776
+		break;
777
+		case RFC2445_TYPE_TEXT:
778
+			// Unescape entities
779
+			$value = strtr($value, array('\\n' => "\n", '\\N' => "\n", '\\\\' => '\\', '\\,' => ',', '\\;' => ';'));
780
+		break;
781
+	}
782
+	return $value;
783 783
 }
784 784
 
785 785
 ?>
Please login to merge, or discard this patch.