Completed
Push — master ( 3281fb...fc273b )
by Hadi
55:41 queued 37:06
created
api/src/WebDAV/Server/Filesystem.php 1 patch
Indentation   +827 added lines, -827 removed lines patch added patch discarded remove patch
@@ -44,793 +44,793 @@  discard block
 block discarded – undo
44 44
  */
45 45
 class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server
46 46
 {
47
-    /**
48
-     * Root directory for WebDAV access
49
-     *
50
-     * Defaults to webserver document root (set by ServeRequest)
51
-     *
52
-     * @access private
53
-     * @var    string
54
-     */
55
-    var $base = "";
56
-
57
-    /**
58
-     * MySQL Host where property and locking information is stored
59
-     *
60
-     * @access private
61
-     * @var    string
62
-     */
63
-    var $db_host = "localhost";
64
-
65
-    /**
66
-     * MySQL database for property/locking information storage
67
-     *
68
-     * @access private
69
-     * @var    string
70
-     */
71
-    var $db_name = "webdav";
72
-
73
-    /**
74
-     * MySQL table name prefix
75
-     *
76
-     * @access private
77
-     * @var    string
78
-     */
79
-    var $db_prefix = "";
80
-
81
-    /**
82
-     * MySQL user for property/locking db access
83
-     *
84
-     * @access private
85
-     * @var    string
86
-     */
87
-    var $db_user = "root";
88
-
89
-    /**
90
-     * MySQL password for property/locking db access
91
-     *
92
-     * @access private
93
-     * @var    string
94
-     */
95
-    var $db_passwd = "";
96
-
97
-    /**
98
-     * Serve a webdav request
99
-     *
100
-     * @access public
101
-     * @param  string
102
-     */
103
-    function ServeRequest($base = false)
104
-    {
105
-        // special treatment for litmus compliance test
106
-        // reply on its identifier header
107
-        // not needed for the test itself but eases debugging
108
-        if (isset($this->_SERVER['HTTP_X_LITMUS'])) {
109
-            error_log("Litmus test ".$this->_SERVER['HTTP_X_LITMUS']);
110
-            header("X-Litmus-reply: ".$this->_SERVER['HTTP_X_LITMUS']);
111
-        }
112
-
113
-        // set root directory, defaults to webserver document root if not set
114
-        if ($base) {
115
-            $this->base = realpath($base); // TODO throw if not a directory
116
-        } else if (!$this->base) {
117
-            $this->base = $this->_SERVER['DOCUMENT_ROOT'];
118
-        }
119
-
120
-        // establish connection to property/locking db
121
-        mysql_connect($this->db_host, $this->db_user, $this->db_passwd) or die(mysql_error());
122
-        mysql_select_db($this->db_name) or die(mysql_error());
123
-        // TODO throw on connection problems
124
-
125
-        // let the base class do all the work
126
-        parent::ServeRequest();
127
-    }
128
-
129
-    /**
130
-     * No authentication is needed here
131
-     *
132
-     * @access private
133
-     * @param  string  HTTP Authentication type (Basic, Digest, ...)
134
-     * @param  string  Username
135
-     * @param  string  Password
136
-     * @return bool    true on successful authentication
137
-     */
138
-    function check_auth($type, $user, $pass)
139
-    {
140
-        return true;
141
-    }
142
-
143
-
144
-    /**
145
-     * PROPFIND method handler
146
-     *
147
-     * @param  array  general parameter passing array
148
-     * @param  array  return array for file properties
149
-     * @return bool   true on success
150
-     */
151
-    function PROPFIND(&$options, &$files)
152
-    {
153
-        // get absolute fs path to requested resource
154
-        $fspath = $this->base . $options["path"];
155
-
156
-        // sanity check
157
-        if (!file_exists($fspath)) {
158
-            return false;
159
-        }
160
-
161
-        // prepare property array
162
-        $files["files"] = array();
163
-
164
-        // store information for the requested path itself
165
-        $files["files"][] = $this->fileinfo($options["path"]);
166
-
167
-        // information for contained resources requested?
168
-        if (!empty($options["depth"]) && is_dir($fspath) && $this->_is_readable($fspath)) {
169
-
170
-            // make sure path ends with '/'
171
-            $options["path"] = $this->_slashify($options["path"]);
172
-
173
-            // try to open directory
174
-            $handle = opendir($fspath);
175
-
176
-            if ($handle) {
177
-                // ok, now get all its contents
178
-                while ($filename = readdir($handle)) {
179
-                    if ($filename != "." && $filename != "..") {
180
-                        $files["files"][] = $this->fileinfo($options["path"].$filename);
181
-                    }
182
-                }
183
-                // TODO recursion needed if "Depth: infinite"
184
-            	closedir($handle);
185
-            }
186
-        }
187
-
188
-        // ok, all done
189
-        return true;
190
-    }
191
-
192
-    /**
193
-     * Get properties for a single file/resource
194
-     *
195
-     * @param  string  resource path
196
-     * @return array   resource properties
197
-     */
198
-    function fileinfo($path)
199
-    {
200
-        // map URI path to filesystem path
201
-        $fspath = $this->base . $path;
202
-
203
-        // create result array
204
-        $info = array();
205
-        // TODO remove slash append code when base clase is able to do it itself
206
-        $info["path"]  = is_dir($fspath) ? $this->_slashify($path) : $path;
207
-        $info["props"] = array();
208
-
209
-        // no special beautified displayname here ...
210
-        $info["props"][] = $this->mkprop("displayname", strtoupper($path));
211
-
212
-        // creation and modification time
213
-        $info["props"][] = $this->mkprop("creationdate",    filectime($fspath));
214
-        $info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
215
-
216
-        // Microsoft extensions: last access time and 'hidden' status
217
-        $info["props"][] = $this->mkprop("lastaccessed",    fileatime($fspath));
218
-        $info["props"][] = $this->mkprop("ishidden", ('.' === substr(basename($fspath), 0, 1)));
219
-
220
-        // type and size (caller already made sure that path exists)
221
-        if (is_dir($fspath)) {
222
-            // directory (WebDAV collection)
223
-            $info["props"][] = $this->mkprop("resourcetype", "collection");
224
-            $info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
225
-        } else {
226
-            // plain file (WebDAV resource)
227
-            $info["props"][] = $this->mkprop("resourcetype", "");
228
-            if ($this->_is_readable($fspath)) {
229
-                $info["props"][] = $this->mkprop("getcontenttype", $this->_mimetype($fspath));
230
-            } else {
231
-                $info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
232
-            }
233
-            $info["props"][] = $this->mkprop("getcontentlength", filesize($fspath));
234
-        }
235
-
236
-        // get additional properties from database
237
-        $query = "SELECT ns, name, value
47
+	/**
48
+	 * Root directory for WebDAV access
49
+	 *
50
+	 * Defaults to webserver document root (set by ServeRequest)
51
+	 *
52
+	 * @access private
53
+	 * @var    string
54
+	 */
55
+	var $base = "";
56
+
57
+	/**
58
+	 * MySQL Host where property and locking information is stored
59
+	 *
60
+	 * @access private
61
+	 * @var    string
62
+	 */
63
+	var $db_host = "localhost";
64
+
65
+	/**
66
+	 * MySQL database for property/locking information storage
67
+	 *
68
+	 * @access private
69
+	 * @var    string
70
+	 */
71
+	var $db_name = "webdav";
72
+
73
+	/**
74
+	 * MySQL table name prefix
75
+	 *
76
+	 * @access private
77
+	 * @var    string
78
+	 */
79
+	var $db_prefix = "";
80
+
81
+	/**
82
+	 * MySQL user for property/locking db access
83
+	 *
84
+	 * @access private
85
+	 * @var    string
86
+	 */
87
+	var $db_user = "root";
88
+
89
+	/**
90
+	 * MySQL password for property/locking db access
91
+	 *
92
+	 * @access private
93
+	 * @var    string
94
+	 */
95
+	var $db_passwd = "";
96
+
97
+	/**
98
+	 * Serve a webdav request
99
+	 *
100
+	 * @access public
101
+	 * @param  string
102
+	 */
103
+	function ServeRequest($base = false)
104
+	{
105
+		// special treatment for litmus compliance test
106
+		// reply on its identifier header
107
+		// not needed for the test itself but eases debugging
108
+		if (isset($this->_SERVER['HTTP_X_LITMUS'])) {
109
+			error_log("Litmus test ".$this->_SERVER['HTTP_X_LITMUS']);
110
+			header("X-Litmus-reply: ".$this->_SERVER['HTTP_X_LITMUS']);
111
+		}
112
+
113
+		// set root directory, defaults to webserver document root if not set
114
+		if ($base) {
115
+			$this->base = realpath($base); // TODO throw if not a directory
116
+		} else if (!$this->base) {
117
+			$this->base = $this->_SERVER['DOCUMENT_ROOT'];
118
+		}
119
+
120
+		// establish connection to property/locking db
121
+		mysql_connect($this->db_host, $this->db_user, $this->db_passwd) or die(mysql_error());
122
+		mysql_select_db($this->db_name) or die(mysql_error());
123
+		// TODO throw on connection problems
124
+
125
+		// let the base class do all the work
126
+		parent::ServeRequest();
127
+	}
128
+
129
+	/**
130
+	 * No authentication is needed here
131
+	 *
132
+	 * @access private
133
+	 * @param  string  HTTP Authentication type (Basic, Digest, ...)
134
+	 * @param  string  Username
135
+	 * @param  string  Password
136
+	 * @return bool    true on successful authentication
137
+	 */
138
+	function check_auth($type, $user, $pass)
139
+	{
140
+		return true;
141
+	}
142
+
143
+
144
+	/**
145
+	 * PROPFIND method handler
146
+	 *
147
+	 * @param  array  general parameter passing array
148
+	 * @param  array  return array for file properties
149
+	 * @return bool   true on success
150
+	 */
151
+	function PROPFIND(&$options, &$files)
152
+	{
153
+		// get absolute fs path to requested resource
154
+		$fspath = $this->base . $options["path"];
155
+
156
+		// sanity check
157
+		if (!file_exists($fspath)) {
158
+			return false;
159
+		}
160
+
161
+		// prepare property array
162
+		$files["files"] = array();
163
+
164
+		// store information for the requested path itself
165
+		$files["files"][] = $this->fileinfo($options["path"]);
166
+
167
+		// information for contained resources requested?
168
+		if (!empty($options["depth"]) && is_dir($fspath) && $this->_is_readable($fspath)) {
169
+
170
+			// make sure path ends with '/'
171
+			$options["path"] = $this->_slashify($options["path"]);
172
+
173
+			// try to open directory
174
+			$handle = opendir($fspath);
175
+
176
+			if ($handle) {
177
+				// ok, now get all its contents
178
+				while ($filename = readdir($handle)) {
179
+					if ($filename != "." && $filename != "..") {
180
+						$files["files"][] = $this->fileinfo($options["path"].$filename);
181
+					}
182
+				}
183
+				// TODO recursion needed if "Depth: infinite"
184
+				closedir($handle);
185
+			}
186
+		}
187
+
188
+		// ok, all done
189
+		return true;
190
+	}
191
+
192
+	/**
193
+	 * Get properties for a single file/resource
194
+	 *
195
+	 * @param  string  resource path
196
+	 * @return array   resource properties
197
+	 */
198
+	function fileinfo($path)
199
+	{
200
+		// map URI path to filesystem path
201
+		$fspath = $this->base . $path;
202
+
203
+		// create result array
204
+		$info = array();
205
+		// TODO remove slash append code when base clase is able to do it itself
206
+		$info["path"]  = is_dir($fspath) ? $this->_slashify($path) : $path;
207
+		$info["props"] = array();
208
+
209
+		// no special beautified displayname here ...
210
+		$info["props"][] = $this->mkprop("displayname", strtoupper($path));
211
+
212
+		// creation and modification time
213
+		$info["props"][] = $this->mkprop("creationdate",    filectime($fspath));
214
+		$info["props"][] = $this->mkprop("getlastmodified", filemtime($fspath));
215
+
216
+		// Microsoft extensions: last access time and 'hidden' status
217
+		$info["props"][] = $this->mkprop("lastaccessed",    fileatime($fspath));
218
+		$info["props"][] = $this->mkprop("ishidden", ('.' === substr(basename($fspath), 0, 1)));
219
+
220
+		// type and size (caller already made sure that path exists)
221
+		if (is_dir($fspath)) {
222
+			// directory (WebDAV collection)
223
+			$info["props"][] = $this->mkprop("resourcetype", "collection");
224
+			$info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory");
225
+		} else {
226
+			// plain file (WebDAV resource)
227
+			$info["props"][] = $this->mkprop("resourcetype", "");
228
+			if ($this->_is_readable($fspath)) {
229
+				$info["props"][] = $this->mkprop("getcontenttype", $this->_mimetype($fspath));
230
+			} else {
231
+				$info["props"][] = $this->mkprop("getcontenttype", "application/x-non-readable");
232
+			}
233
+			$info["props"][] = $this->mkprop("getcontentlength", filesize($fspath));
234
+		}
235
+
236
+		// get additional properties from database
237
+		$query = "SELECT ns, name, value
238 238
                         FROM {$this->db_prefix}properties
239 239
                        WHERE path = '$path'";
240
-        $res = mysql_query($query);
241
-        while ($row = mysql_fetch_assoc($res)) {
242
-            $info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]);
243
-        }
244
-        mysql_free_result($res);
245
-
246
-        return $info;
247
-    }
248
-
249
-    /**
250
-     * detect if a given program is found in the search PATH
251
-     *
252
-     * helper function used by _mimetype() to detect if the
253
-     * external 'file' utility is available
254
-     *
255
-     * @param  string  program name
256
-     * @param  string  optional search path, defaults to $PATH
257
-     * @return bool    true if executable program found in path
258
-     */
259
-    function _can_execute($name, $path = false)
260
-    {
261
-        // path defaults to PATH from environment if not set
262
-        if ($path === false) {
263
-            $path = getenv("PATH");
264
-        }
265
-
266
-        // check method depends on operating system
267
-        if (!strncmp(PHP_OS, "WIN", 3)) {
268
-            // on Windows an appropriate COM or EXE file needs to exist
269
-            $exts     = array(".exe", ".com");
270
-            $check_fn = "file_exists";
271
-        } else {
272
-            // anywhere else we look for an executable file of that name
273
-            $exts     = array("");
274
-            $check_fn = "is_executable";
275
-        }
276
-
277
-        // now check the directories in the path for the program
278
-        foreach (explode(PATH_SEPARATOR, $path) as $dir) {
279
-            // skip invalid path entries
280
-            if (!file_exists($dir)) continue;
281
-            if (!is_dir($dir)) continue;
282
-
283
-            // and now look for the file
284
-            foreach ($exts as $ext) {
285
-                if ($check_fn("$dir/$name".$ext)) return true;
286
-            }
287
-        }
288
-
289
-        return false;
290
-    }
291
-
292
-    /**
293
-     * Check if path is readable by current user
294
-     *
295
-     * Allow extending classes to overwrite it
296
-     *
297
-     * @param string $fspath
298
-     * @return boolean
299
-     */
300
-    function _is_readable($fspath)
301
-    {
302
-    	return is_readable($fspath);
303
-    }
304
-
305
-    /**
306
-     * Check if path is writable by current user
307
-     *
308
-     * Allow extending classes to overwrite it
309
-     *
310
-     * @param string $fspath
311
-     * @return boolean
312
-     */
313
-    function _is_writable($fspath)
314
-    {
315
-    	return is_writable($fspath);
316
-    }
317
-
318
-    /**
319
-     * try to detect the mime type of a file
320
-     *
321
-     * @param  string  file path
322
-     * @return string  guessed mime type
323
-     */
324
-    function _mimetype($fspath)
325
-    {
326
-        if (is_dir($fspath)) {
327
-            // directories are easy
328
-            return "httpd/unix-directory";
329
-        } else if (function_exists("mime_content_type")) {
330
-            // use mime magic extension if available
331
-            $mime_type = mime_content_type($fspath);
332
-        } else if ($this->_can_execute("file")) {
333
-            // it looks like we have a 'file' command,
334
-            // lets see it it does have mime support
335
-            $fp    = popen("file -i '$fspath' 2>/dev/null", "r");
336
-            $reply = fgets($fp);
337
-            pclose($fp);
338
-
339
-            // popen will not return an error if the binary was not found
340
-            // and find may not have mime support using "-i"
341
-            // so we test the format of the returned string
342
-
343
-            // the reply begins with the requested filename
344
-            if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) {
345
-                $reply = substr($reply, strlen($fspath)+2);
346
-                // followed by the mime type (maybe including options)
347
-                if (preg_match('|^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*|', $reply, $matches)) {
348
-                    $mime_type = $matches[0];
349
-                }
350
-            }
351
-        }
352
-
353
-        if (empty($mime_type)) {
354
-            // Fallback solution: try to guess the type by the file extension
355
-            // TODO: add more ...
356
-            // TODO: it has been suggested to delegate mimetype detection
357
-            //       to apache but this has at least three issues:
358
-            //       - works only with apache
359
-            //       - needs file to be within the document tree
360
-            //       - requires apache mod_magic
361
-            // TODO: can we use the registry for this on Windows?
362
-            //       OTOH if the server is Windos the clients are likely to
363
-            //       be Windows, too, and tend do ignore the Content-Type
364
-            //       anyway (overriding it with information taken from
365
-            //       the registry)
366
-            // TODO: have a seperate PEAR class for mimetype detection?
367
-            switch (strtolower(strrchr(basename($fspath), "."))) {
368
-            case ".html":
369
-                $mime_type = "text/html";
370
-                break;
371
-            case ".gif":
372
-                $mime_type = "image/gif";
373
-                break;
374
-            case ".jpg":
375
-                $mime_type = "image/jpeg";
376
-                break;
377
-            default:
378
-                $mime_type = "application/octet-stream";
379
-                break;
380
-            }
381
-        }
382
-
383
-        return $mime_type;
384
-    }
385
-
386
-    /**
387
-     * HEAD method handler
388
-     *
389
-     * @param  array  parameter passing array
390
-     * @return bool   true on success
391
-     */
392
-    function HEAD(&$options)
393
-    {
394
-        // get absolute fs path to requested resource
395
-        $fspath = $this->base . $options["path"];
396
-
397
-        // sanity check
398
-        if (!file_exists($fspath)) return false;
399
-
400
-        // detect resource type
401
-        $options['mimetype'] = $this->_mimetype($fspath);
402
-
403
-        // detect modification time
404
-        // see rfc2518, section 13.7
405
-        // some clients seem to treat this as a reverse rule
406
-        // requiering a Last-Modified header if the getlastmodified header was set
407
-        $options['mtime'] = filemtime($fspath);
408
-
409
-        // detect resource size
410
-        $options['size'] = filesize($fspath);
411
-
412
-        return true;
413
-    }
414
-
415
-    /**
416
-     * GET method handler
417
-     *
418
-     * @param  array  parameter passing array
419
-     * @return bool   true on success
420
-     */
421
-    function GET(&$options)
422
-    {
423
-        // get absolute fs path to requested resource
424
-        $fspath = $this->base . $options["path"];
425
-
426
-        // is this a collection?
427
-        if (is_dir($fspath)) {
428
-            return $this->GetDir($fspath, $options);
429
-        }
430
-
431
-        // the header output is the same as for HEAD
432
-        if (!$this->HEAD($options)) {
433
-            return false;
434
-        }
435
-
436
-        // no need to check result here, it is handled by the base class
437
-        $options['stream'] = fopen($fspath, "r");
438
-
439
-        return true;
440
-    }
441
-
442
-    /**
443
-     * GET method handler for directories
444
-     *
445
-     * This is a very simple mod_index lookalike.
446
-     * See RFC 2518, Section 8.4 on GET/HEAD for collections
447
-     *
448
-     * @param  string  directory path
449
-     * @return void    function has to handle HTTP response itself
450
-     */
451
-    function GetDir($fspath, &$options)
452
-    {
453
-        $path = $this->_slashify($options["path"]);
454
-        if ($path != $options["path"]) {
455
-            header("Location: ".$this->base_uri.$path);
456
-            exit;
457
-        }
458
-
459
-        // fixed width directory column format
460
-        $format = "%15s  %-19s  %-s\n";
461
-
462
-        if (!$this->_is_readable($fspath)) {
463
-            return false;
464
-        }
465
-
466
-        $handle = opendir($fspath);
467
-        if (!$handle) {
468
-            return false;
469
-        }
470
-
471
-        echo "<html><head><title>Index of ".htmlspecialchars(urldecode($options['path']))."</title></head>\n";
472
-
473
-        echo "<h1>Index of ".htmlspecialchars($options['path'])."</h1>\n";
474
-
475
-        echo "<pre>";
476
-        printf($format, "Size", "Last modified", "Filename");
477
-        echo "<hr>";
478
-
479
-        while ($filename = readdir($handle)) {
480
-            if ($filename != "." && $filename != "..") {
481
-                $fullpath = $fspath.$filename;
482
-                $name     = htmlspecialchars($filename);
483
-                printf($format,
484
-                       number_format(filesize($fullpath)),
485
-                       strftime("%Y-%m-%d %H:%M:%S", filemtime($fullpath)),
486
-                       '<a href="'.$name.'">'.urldecode($name).'</a>');
487
-            }
488
-        }
489
-
490
-        echo "</pre>";
491
-
492
-        closedir($handle);
493
-
494
-        echo "</html>\n";
495
-
496
-        exit;
497
-    }
498
-
499
-    /**
500
-     * PUT method handler
501
-     *
502
-     * @param  array  parameter passing array
503
-     * @return bool   true on success
504
-     */
505
-    function PUT(&$options)
506
-    {
507
-        $fspath = $this->base . $options["path"];
508
-
509
-        $dir = dirname($fspath);
510
-        if (!file_exists($dir) || !is_dir($dir)) {
511
-            return "409 Conflict"; // TODO right status code for both?
512
-        }
513
-
514
-        $options["new"] = ! file_exists($fspath);
515
-
516
-        if ($options["new"] && !$this->_is_writable($dir)) {
517
-            return "403 Forbidden";
518
-        }
519
-        if (!$options["new"] && !$this->_is_writable($fspath)) {
520
-            return "403 Forbidden";
521
-        }
522
-        if (!$options["new"] && is_dir($fspath)) {
523
-            return "403 Forbidden";
524
-        }
525
-
526
-        $fp = fopen($fspath, "w");
527
-
528
-        return $fp;
529
-    }
530
-
531
-
532
-    /**
533
-     * MKCOL method handler
534
-     *
535
-     * @param  array  general parameter passing array
536
-     * @return bool   true on success
537
-     */
538
-    function MKCOL($options)
539
-    {
540
-        $path   = $this->base .$options["path"];
541
-        $parent = dirname($path);
542
-        $name   = basename($path);
543
-
544
-        if (!file_exists($parent)) {
545
-            return "409 Conflict";
546
-        }
547
-
548
-        if (!is_dir($parent)) {
549
-            return "403 Forbidden";
550
-        }
551
-
552
-        if ( file_exists($parent."/".$name) ) {
553
-            return "405 Method not allowed";
554
-        }
555
-
556
-        if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
557
-            return "415 Unsupported media type";
558
-        }
559
-
560
-        $stat = mkdir($parent."/".$name, 0777);
561
-        if (!$stat) {
562
-            return "403 Forbidden";
563
-        }
564
-
565
-        return ("201 Created");
566
-    }
567
-
568
-
569
-    /**
570
-     * DELETE method handler
571
-     *
572
-     * @param  array  general parameter passing array
573
-     * @return bool   true on success
574
-     */
575
-    function DELETE($options)
576
-    {
577
-        $path = $this->base . "/" .$options["path"];
578
-
579
-        if (!file_exists($path)) {
580
-            return "404 Not found";
581
-        }
582
-
583
-        if (is_dir($path)) {
584
-            $query = "DELETE FROM {$this->db_prefix}properties
240
+		$res = mysql_query($query);
241
+		while ($row = mysql_fetch_assoc($res)) {
242
+			$info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]);
243
+		}
244
+		mysql_free_result($res);
245
+
246
+		return $info;
247
+	}
248
+
249
+	/**
250
+	 * detect if a given program is found in the search PATH
251
+	 *
252
+	 * helper function used by _mimetype() to detect if the
253
+	 * external 'file' utility is available
254
+	 *
255
+	 * @param  string  program name
256
+	 * @param  string  optional search path, defaults to $PATH
257
+	 * @return bool    true if executable program found in path
258
+	 */
259
+	function _can_execute($name, $path = false)
260
+	{
261
+		// path defaults to PATH from environment if not set
262
+		if ($path === false) {
263
+			$path = getenv("PATH");
264
+		}
265
+
266
+		// check method depends on operating system
267
+		if (!strncmp(PHP_OS, "WIN", 3)) {
268
+			// on Windows an appropriate COM or EXE file needs to exist
269
+			$exts     = array(".exe", ".com");
270
+			$check_fn = "file_exists";
271
+		} else {
272
+			// anywhere else we look for an executable file of that name
273
+			$exts     = array("");
274
+			$check_fn = "is_executable";
275
+		}
276
+
277
+		// now check the directories in the path for the program
278
+		foreach (explode(PATH_SEPARATOR, $path) as $dir) {
279
+			// skip invalid path entries
280
+			if (!file_exists($dir)) continue;
281
+			if (!is_dir($dir)) continue;
282
+
283
+			// and now look for the file
284
+			foreach ($exts as $ext) {
285
+				if ($check_fn("$dir/$name".$ext)) return true;
286
+			}
287
+		}
288
+
289
+		return false;
290
+	}
291
+
292
+	/**
293
+	 * Check if path is readable by current user
294
+	 *
295
+	 * Allow extending classes to overwrite it
296
+	 *
297
+	 * @param string $fspath
298
+	 * @return boolean
299
+	 */
300
+	function _is_readable($fspath)
301
+	{
302
+		return is_readable($fspath);
303
+	}
304
+
305
+	/**
306
+	 * Check if path is writable by current user
307
+	 *
308
+	 * Allow extending classes to overwrite it
309
+	 *
310
+	 * @param string $fspath
311
+	 * @return boolean
312
+	 */
313
+	function _is_writable($fspath)
314
+	{
315
+		return is_writable($fspath);
316
+	}
317
+
318
+	/**
319
+	 * try to detect the mime type of a file
320
+	 *
321
+	 * @param  string  file path
322
+	 * @return string  guessed mime type
323
+	 */
324
+	function _mimetype($fspath)
325
+	{
326
+		if (is_dir($fspath)) {
327
+			// directories are easy
328
+			return "httpd/unix-directory";
329
+		} else if (function_exists("mime_content_type")) {
330
+			// use mime magic extension if available
331
+			$mime_type = mime_content_type($fspath);
332
+		} else if ($this->_can_execute("file")) {
333
+			// it looks like we have a 'file' command,
334
+			// lets see it it does have mime support
335
+			$fp    = popen("file -i '$fspath' 2>/dev/null", "r");
336
+			$reply = fgets($fp);
337
+			pclose($fp);
338
+
339
+			// popen will not return an error if the binary was not found
340
+			// and find may not have mime support using "-i"
341
+			// so we test the format of the returned string
342
+
343
+			// the reply begins with the requested filename
344
+			if (!strncmp($reply, "$fspath: ", strlen($fspath)+2)) {
345
+				$reply = substr($reply, strlen($fspath)+2);
346
+				// followed by the mime type (maybe including options)
347
+				if (preg_match('|^[[:alnum:]_-]+/[[:alnum:]_-]+;?.*|', $reply, $matches)) {
348
+					$mime_type = $matches[0];
349
+				}
350
+			}
351
+		}
352
+
353
+		if (empty($mime_type)) {
354
+			// Fallback solution: try to guess the type by the file extension
355
+			// TODO: add more ...
356
+			// TODO: it has been suggested to delegate mimetype detection
357
+			//       to apache but this has at least three issues:
358
+			//       - works only with apache
359
+			//       - needs file to be within the document tree
360
+			//       - requires apache mod_magic
361
+			// TODO: can we use the registry for this on Windows?
362
+			//       OTOH if the server is Windos the clients are likely to
363
+			//       be Windows, too, and tend do ignore the Content-Type
364
+			//       anyway (overriding it with information taken from
365
+			//       the registry)
366
+			// TODO: have a seperate PEAR class for mimetype detection?
367
+			switch (strtolower(strrchr(basename($fspath), "."))) {
368
+			case ".html":
369
+				$mime_type = "text/html";
370
+				break;
371
+			case ".gif":
372
+				$mime_type = "image/gif";
373
+				break;
374
+			case ".jpg":
375
+				$mime_type = "image/jpeg";
376
+				break;
377
+			default:
378
+				$mime_type = "application/octet-stream";
379
+				break;
380
+			}
381
+		}
382
+
383
+		return $mime_type;
384
+	}
385
+
386
+	/**
387
+	 * HEAD method handler
388
+	 *
389
+	 * @param  array  parameter passing array
390
+	 * @return bool   true on success
391
+	 */
392
+	function HEAD(&$options)
393
+	{
394
+		// get absolute fs path to requested resource
395
+		$fspath = $this->base . $options["path"];
396
+
397
+		// sanity check
398
+		if (!file_exists($fspath)) return false;
399
+
400
+		// detect resource type
401
+		$options['mimetype'] = $this->_mimetype($fspath);
402
+
403
+		// detect modification time
404
+		// see rfc2518, section 13.7
405
+		// some clients seem to treat this as a reverse rule
406
+		// requiering a Last-Modified header if the getlastmodified header was set
407
+		$options['mtime'] = filemtime($fspath);
408
+
409
+		// detect resource size
410
+		$options['size'] = filesize($fspath);
411
+
412
+		return true;
413
+	}
414
+
415
+	/**
416
+	 * GET method handler
417
+	 *
418
+	 * @param  array  parameter passing array
419
+	 * @return bool   true on success
420
+	 */
421
+	function GET(&$options)
422
+	{
423
+		// get absolute fs path to requested resource
424
+		$fspath = $this->base . $options["path"];
425
+
426
+		// is this a collection?
427
+		if (is_dir($fspath)) {
428
+			return $this->GetDir($fspath, $options);
429
+		}
430
+
431
+		// the header output is the same as for HEAD
432
+		if (!$this->HEAD($options)) {
433
+			return false;
434
+		}
435
+
436
+		// no need to check result here, it is handled by the base class
437
+		$options['stream'] = fopen($fspath, "r");
438
+
439
+		return true;
440
+	}
441
+
442
+	/**
443
+	 * GET method handler for directories
444
+	 *
445
+	 * This is a very simple mod_index lookalike.
446
+	 * See RFC 2518, Section 8.4 on GET/HEAD for collections
447
+	 *
448
+	 * @param  string  directory path
449
+	 * @return void    function has to handle HTTP response itself
450
+	 */
451
+	function GetDir($fspath, &$options)
452
+	{
453
+		$path = $this->_slashify($options["path"]);
454
+		if ($path != $options["path"]) {
455
+			header("Location: ".$this->base_uri.$path);
456
+			exit;
457
+		}
458
+
459
+		// fixed width directory column format
460
+		$format = "%15s  %-19s  %-s\n";
461
+
462
+		if (!$this->_is_readable($fspath)) {
463
+			return false;
464
+		}
465
+
466
+		$handle = opendir($fspath);
467
+		if (!$handle) {
468
+			return false;
469
+		}
470
+
471
+		echo "<html><head><title>Index of ".htmlspecialchars(urldecode($options['path']))."</title></head>\n";
472
+
473
+		echo "<h1>Index of ".htmlspecialchars($options['path'])."</h1>\n";
474
+
475
+		echo "<pre>";
476
+		printf($format, "Size", "Last modified", "Filename");
477
+		echo "<hr>";
478
+
479
+		while ($filename = readdir($handle)) {
480
+			if ($filename != "." && $filename != "..") {
481
+				$fullpath = $fspath.$filename;
482
+				$name     = htmlspecialchars($filename);
483
+				printf($format,
484
+					   number_format(filesize($fullpath)),
485
+					   strftime("%Y-%m-%d %H:%M:%S", filemtime($fullpath)),
486
+					   '<a href="'.$name.'">'.urldecode($name).'</a>');
487
+			}
488
+		}
489
+
490
+		echo "</pre>";
491
+
492
+		closedir($handle);
493
+
494
+		echo "</html>\n";
495
+
496
+		exit;
497
+	}
498
+
499
+	/**
500
+	 * PUT method handler
501
+	 *
502
+	 * @param  array  parameter passing array
503
+	 * @return bool   true on success
504
+	 */
505
+	function PUT(&$options)
506
+	{
507
+		$fspath = $this->base . $options["path"];
508
+
509
+		$dir = dirname($fspath);
510
+		if (!file_exists($dir) || !is_dir($dir)) {
511
+			return "409 Conflict"; // TODO right status code for both?
512
+		}
513
+
514
+		$options["new"] = ! file_exists($fspath);
515
+
516
+		if ($options["new"] && !$this->_is_writable($dir)) {
517
+			return "403 Forbidden";
518
+		}
519
+		if (!$options["new"] && !$this->_is_writable($fspath)) {
520
+			return "403 Forbidden";
521
+		}
522
+		if (!$options["new"] && is_dir($fspath)) {
523
+			return "403 Forbidden";
524
+		}
525
+
526
+		$fp = fopen($fspath, "w");
527
+
528
+		return $fp;
529
+	}
530
+
531
+
532
+	/**
533
+	 * MKCOL method handler
534
+	 *
535
+	 * @param  array  general parameter passing array
536
+	 * @return bool   true on success
537
+	 */
538
+	function MKCOL($options)
539
+	{
540
+		$path   = $this->base .$options["path"];
541
+		$parent = dirname($path);
542
+		$name   = basename($path);
543
+
544
+		if (!file_exists($parent)) {
545
+			return "409 Conflict";
546
+		}
547
+
548
+		if (!is_dir($parent)) {
549
+			return "403 Forbidden";
550
+		}
551
+
552
+		if ( file_exists($parent."/".$name) ) {
553
+			return "405 Method not allowed";
554
+		}
555
+
556
+		if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
557
+			return "415 Unsupported media type";
558
+		}
559
+
560
+		$stat = mkdir($parent."/".$name, 0777);
561
+		if (!$stat) {
562
+			return "403 Forbidden";
563
+		}
564
+
565
+		return ("201 Created");
566
+	}
567
+
568
+
569
+	/**
570
+	 * DELETE method handler
571
+	 *
572
+	 * @param  array  general parameter passing array
573
+	 * @return bool   true on success
574
+	 */
575
+	function DELETE($options)
576
+	{
577
+		$path = $this->base . "/" .$options["path"];
578
+
579
+		if (!file_exists($path)) {
580
+			return "404 Not found";
581
+		}
582
+
583
+		if (is_dir($path)) {
584
+			$query = "DELETE FROM {$this->db_prefix}properties
585 585
                            WHERE path LIKE '".$this->_slashify($options["path"])."%'";
586
-            mysql_query($query);
587
-            System::rm(array("-rf", $path));
588
-        } else {
589
-            unlink($path);
590
-        }
591
-        $query = "DELETE FROM {$this->db_prefix}properties
586
+			mysql_query($query);
587
+			System::rm(array("-rf", $path));
588
+		} else {
589
+			unlink($path);
590
+		}
591
+		$query = "DELETE FROM {$this->db_prefix}properties
592 592
                        WHERE path = '$options[path]'";
593
-        mysql_query($query);
594
-
595
-        return "204 No Content";
596
-    }
597
-
598
-
599
-    /**
600
-     * MOVE method handler
601
-     *
602
-     * @param  array  general parameter passing array
603
-     * @return bool   true on success
604
-     */
605
-    function MOVE($options)
606
-    {
607
-        return $this->COPY($options, true);
608
-    }
609
-
610
-    /**
611
-     * COPY method handler
612
-     *
613
-     * @param  array  general parameter passing array
614
-     * @return bool   true on success
615
-     */
616
-    function COPY($options, $del=false)
617
-    {
618
-        // TODO Property updates still broken (Litmus should detect this?)
619
-
620
-        if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
621
-            return "415 Unsupported media type";
622
-        }
623
-
624
-        // no copying to different WebDAV Servers yet
625
-        if (isset($options["dest_url"])) {
626
-            return "502 bad gateway";
627
-        }
628
-
629
-        $source = $this->base . $options["path"];
630
-        if (!file_exists($source)) {
631
-            return "404 Not found";
632
-        }
633
-
634
-        if (is_dir($source)) { // resource is a collection
635
-            switch ($options["depth"]) {
636
-            case "infinity": // valid
637
-                break;
638
-            case "0": // valid for COPY only
639
-                if ($del) { // MOVE?
640
-                    return "400 Bad request";
641
-                }
642
-                break;
643
-            case "1": // invalid for both COPY and MOVE
644
-            default:
645
-                return "400 Bad request";
646
-            }
647
-        }
648
-
649
-        $dest         = $this->base . $options["dest"];
650
-        $destdir      = dirname($dest);
651
-
652
-        if (!file_exists($destdir) || !is_dir($destdir)) {
653
-            return "409 Conflict";
654
-        }
655
-
656
-
657
-        $new          = !file_exists($dest);
658
-        $existing_col = false;
659
-
660
-        if (!$new) {
661
-            if ($del && is_dir($dest)) {
662
-                if (!$options["overwrite"]) {
663
-                    return "412 precondition failed";
664
-                }
665
-                $dest .= basename($source);
666
-                if (file_exists($dest)) {
667
-                    $options["dest"] .= basename($source);
668
-                } else {
669
-                    $new          = true;
670
-                    $existing_col = true;
671
-                }
672
-            }
673
-        }
674
-
675
-        if (!$new) {
676
-            if ($options["overwrite"]) {
677
-                $stat = $this->DELETE(array("path" => $options["dest"]));
678
-                if (($stat{0} != "2") && (substr($stat, 0, 3) != "404")) {
679
-                    return $stat;
680
-                }
681
-            } else {
682
-                return "412 precondition failed";
683
-            }
684
-        }
685
-
686
-        if ($del) {
687
-            if (!rename($source, $dest)) {
688
-                return "500 Internal server error";
689
-            }
690
-            $destpath = $this->_unslashify($options["dest"]);
691
-            if (is_dir($source)) {
692
-                $query = "UPDATE {$this->db_prefix}properties
593
+		mysql_query($query);
594
+
595
+		return "204 No Content";
596
+	}
597
+
598
+
599
+	/**
600
+	 * MOVE method handler
601
+	 *
602
+	 * @param  array  general parameter passing array
603
+	 * @return bool   true on success
604
+	 */
605
+	function MOVE($options)
606
+	{
607
+		return $this->COPY($options, true);
608
+	}
609
+
610
+	/**
611
+	 * COPY method handler
612
+	 *
613
+	 * @param  array  general parameter passing array
614
+	 * @return bool   true on success
615
+	 */
616
+	function COPY($options, $del=false)
617
+	{
618
+		// TODO Property updates still broken (Litmus should detect this?)
619
+
620
+		if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
621
+			return "415 Unsupported media type";
622
+		}
623
+
624
+		// no copying to different WebDAV Servers yet
625
+		if (isset($options["dest_url"])) {
626
+			return "502 bad gateway";
627
+		}
628
+
629
+		$source = $this->base . $options["path"];
630
+		if (!file_exists($source)) {
631
+			return "404 Not found";
632
+		}
633
+
634
+		if (is_dir($source)) { // resource is a collection
635
+			switch ($options["depth"]) {
636
+			case "infinity": // valid
637
+				break;
638
+			case "0": // valid for COPY only
639
+				if ($del) { // MOVE?
640
+					return "400 Bad request";
641
+				}
642
+				break;
643
+			case "1": // invalid for both COPY and MOVE
644
+			default:
645
+				return "400 Bad request";
646
+			}
647
+		}
648
+
649
+		$dest         = $this->base . $options["dest"];
650
+		$destdir      = dirname($dest);
651
+
652
+		if (!file_exists($destdir) || !is_dir($destdir)) {
653
+			return "409 Conflict";
654
+		}
655
+
656
+
657
+		$new          = !file_exists($dest);
658
+		$existing_col = false;
659
+
660
+		if (!$new) {
661
+			if ($del && is_dir($dest)) {
662
+				if (!$options["overwrite"]) {
663
+					return "412 precondition failed";
664
+				}
665
+				$dest .= basename($source);
666
+				if (file_exists($dest)) {
667
+					$options["dest"] .= basename($source);
668
+				} else {
669
+					$new          = true;
670
+					$existing_col = true;
671
+				}
672
+			}
673
+		}
674
+
675
+		if (!$new) {
676
+			if ($options["overwrite"]) {
677
+				$stat = $this->DELETE(array("path" => $options["dest"]));
678
+				if (($stat{0} != "2") && (substr($stat, 0, 3) != "404")) {
679
+					return $stat;
680
+				}
681
+			} else {
682
+				return "412 precondition failed";
683
+			}
684
+		}
685
+
686
+		if ($del) {
687
+			if (!rename($source, $dest)) {
688
+				return "500 Internal server error";
689
+			}
690
+			$destpath = $this->_unslashify($options["dest"]);
691
+			if (is_dir($source)) {
692
+				$query = "UPDATE {$this->db_prefix}properties
693 693
                                  SET path = REPLACE(path, '".$options["path"]."', '".$destpath."')
694 694
                                WHERE path LIKE '".$this->_slashify($options["path"])."%'";
695
-                mysql_query($query);
696
-            }
695
+				mysql_query($query);
696
+			}
697 697
 
698
-            $query = "UPDATE {$this->db_prefix}properties
698
+			$query = "UPDATE {$this->db_prefix}properties
699 699
                              SET path = '".$destpath."'
700 700
                            WHERE path = '".$options["path"]."'";
701
-            mysql_query($query);
702
-        } else {
703
-            if (is_dir($source) && $options["depth"] == "infinity") {	// no find for depth="0"
704
-                $files = System::find($source);
705
-                $files = array_reverse($files);
706
-            } else {
707
-                $files = array($source);
708
-            }
709
-
710
-            if (!is_array($files) || empty($files)) {
711
-                return "500 Internal server error";
712
-            }
713
-
714
-
715
-            foreach ($files as $file) {
716
-                if (is_dir($file)) {
717
-                    $file = $this->_slashify($file);
718
-                }
719
-
720
-                $destfile = str_replace($source, $dest, $file);
721
-
722
-                if (is_dir($file)) {
723
-                    if (!file_exists($destfile)) {
724
-                        if (!$this->_is_writable(dirname($destfile))) {
725
-                            return "403 Forbidden";
726
-                        }
727
-                        if (!mkdir($destfile)) {
728
-                            return "409 Conflict";
729
-                        }
730
-                    } else if (!is_dir($destfile)) {
731
-                        return "409 Conflict";
732
-                    }
733
-                } else {
734
-
735
-                    if (!copy($file, $destfile)) {
736
-                        return "409 Conflict";
737
-                    }
738
-                }
739
-            }
740
-
741
-            $query = "INSERT INTO {$this->db_prefix}properties
701
+			mysql_query($query);
702
+		} else {
703
+			if (is_dir($source) && $options["depth"] == "infinity") {	// no find for depth="0"
704
+				$files = System::find($source);
705
+				$files = array_reverse($files);
706
+			} else {
707
+				$files = array($source);
708
+			}
709
+
710
+			if (!is_array($files) || empty($files)) {
711
+				return "500 Internal server error";
712
+			}
713
+
714
+
715
+			foreach ($files as $file) {
716
+				if (is_dir($file)) {
717
+					$file = $this->_slashify($file);
718
+				}
719
+
720
+				$destfile = str_replace($source, $dest, $file);
721
+
722
+				if (is_dir($file)) {
723
+					if (!file_exists($destfile)) {
724
+						if (!$this->_is_writable(dirname($destfile))) {
725
+							return "403 Forbidden";
726
+						}
727
+						if (!mkdir($destfile)) {
728
+							return "409 Conflict";
729
+						}
730
+					} else if (!is_dir($destfile)) {
731
+						return "409 Conflict";
732
+					}
733
+				} else {
734
+
735
+					if (!copy($file, $destfile)) {
736
+						return "409 Conflict";
737
+					}
738
+				}
739
+			}
740
+
741
+			$query = "INSERT INTO {$this->db_prefix}properties
742 742
                                SELECT *
743 743
                                  FROM {$this->db_prefix}properties
744 744
                                 WHERE path = '".$options['path']."'";
745
-        }
746
-
747
-        return ($new && !$existing_col) ? "201 Created" : "204 No Content";
748
-    }
749
-
750
-    /**
751
-     * PROPPATCH method handler
752
-     *
753
-     * @param  array  general parameter passing array
754
-     * @return bool   true on success
755
-     */
756
-    function PROPPATCH(&$options)
757
-    {
758
-        global $prefs, $tab;
759
-
760
-        $msg  = "";
761
-        $path = $options["path"];
762
-        $dir  = dirname($path)."/";
763
-        $base = basename($path);
764
-
765
-        foreach ($options["props"] as $key => $prop) {
766
-            if ($prop["ns"] == "DAV:") {
767
-                $options["props"][$key]['status'] = "403 Forbidden";
768
-            } else {
769
-                if (isset($prop["val"])) {
770
-                    $query = "REPLACE INTO {$this->db_prefix}properties
745
+		}
746
+
747
+		return ($new && !$existing_col) ? "201 Created" : "204 No Content";
748
+	}
749
+
750
+	/**
751
+	 * PROPPATCH method handler
752
+	 *
753
+	 * @param  array  general parameter passing array
754
+	 * @return bool   true on success
755
+	 */
756
+	function PROPPATCH(&$options)
757
+	{
758
+		global $prefs, $tab;
759
+
760
+		$msg  = "";
761
+		$path = $options["path"];
762
+		$dir  = dirname($path)."/";
763
+		$base = basename($path);
764
+
765
+		foreach ($options["props"] as $key => $prop) {
766
+			if ($prop["ns"] == "DAV:") {
767
+				$options["props"][$key]['status'] = "403 Forbidden";
768
+			} else {
769
+				if (isset($prop["val"])) {
770
+					$query = "REPLACE INTO {$this->db_prefix}properties
771 771
                                            SET path = '$options[path]'
772 772
                                              , name = '$prop[name]'
773 773
                                              , ns= '$prop[ns]'
774 774
                                              , value = '$prop[val]'";
775
-                } else {
776
-                    $query = "DELETE FROM {$this->db_prefix}properties
775
+				} else {
776
+					$query = "DELETE FROM {$this->db_prefix}properties
777 777
                                         WHERE path = '$options[path]'
778 778
                                           AND name = '$prop[name]'
779 779
                                           AND ns = '$prop[ns]'";
780
-                }
781
-                mysql_query($query);
782
-            }
783
-        }
784
-
785
-        return "";
786
-    }
787
-
788
-
789
-    /**
790
-     * LOCK method handler
791
-     *
792
-     * @param  array  general parameter passing array
793
-     * @return bool   true on success
794
-     */
795
-    function LOCK(&$options)
796
-    {
797
-        // get absolute fs path to requested resource
798
-        $fspath = $this->base . $options["path"];
799
-
800
-        // TODO recursive locks on directories not supported yet
801
-        // makes litmus test "32. lock_collection" fail
802
-        if (is_dir($fspath) && !empty($options["depth"])) {
803
-            return "409 Conflict";
804
-        }
805
-
806
-        $options["timeout"] = time()+300; // 5min. hardcoded
807
-
808
-        if (isset($options["update"])) { // Lock Update
809
-            $where = "WHERE path = '$options[path]' AND token = '$options[update]'";
810
-
811
-            $query = "SELECT owner, exclusivelock FROM {$this->db_prefix}locks $where";
812
-            $res   = mysql_query($query);
813
-            $row   = mysql_fetch_assoc($res);
814
-            mysql_free_result($res);
815
-
816
-            if (is_array($row)) {
817
-                $query = "UPDATE {$this->db_prefix}locks
780
+				}
781
+				mysql_query($query);
782
+			}
783
+		}
784
+
785
+		return "";
786
+	}
787
+
788
+
789
+	/**
790
+	 * LOCK method handler
791
+	 *
792
+	 * @param  array  general parameter passing array
793
+	 * @return bool   true on success
794
+	 */
795
+	function LOCK(&$options)
796
+	{
797
+		// get absolute fs path to requested resource
798
+		$fspath = $this->base . $options["path"];
799
+
800
+		// TODO recursive locks on directories not supported yet
801
+		// makes litmus test "32. lock_collection" fail
802
+		if (is_dir($fspath) && !empty($options["depth"])) {
803
+			return "409 Conflict";
804
+		}
805
+
806
+		$options["timeout"] = time()+300; // 5min. hardcoded
807
+
808
+		if (isset($options["update"])) { // Lock Update
809
+			$where = "WHERE path = '$options[path]' AND token = '$options[update]'";
810
+
811
+			$query = "SELECT owner, exclusivelock FROM {$this->db_prefix}locks $where";
812
+			$res   = mysql_query($query);
813
+			$row   = mysql_fetch_assoc($res);
814
+			mysql_free_result($res);
815
+
816
+			if (is_array($row)) {
817
+				$query = "UPDATE {$this->db_prefix}locks
818 818
                                  SET expires = '$options[timeout]'
819 819
                                    , modified = ".time()."
820 820
                               $where";
821
-                mysql_query($query);
821
+				mysql_query($query);
822 822
 
823
-                $options['owner'] = $row['owner'];
824
-                $options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared";
825
-                $options['type']  = $row["exclusivelock"] ? "write"     : "read";
823
+				$options['owner'] = $row['owner'];
824
+				$options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared";
825
+				$options['type']  = $row["exclusivelock"] ? "write"     : "read";
826 826
 
827
-                return true;
828
-            } else {
829
-                return false;
830
-            }
831
-        }
827
+				return true;
828
+			} else {
829
+				return false;
830
+			}
831
+		}
832 832
 
833
-        $query = "INSERT INTO {$this->db_prefix}locks
833
+		$query = "INSERT INTO {$this->db_prefix}locks
834 834
                         SET token   = '$options[locktoken]'
835 835
                           , path    = '$options[path]'
836 836
                           , created = ".time()."
@@ -838,76 +838,76 @@  discard block
 block discarded – undo
838 838
                           , owner   = '$options[owner]'
839 839
                           , expires = '$options[timeout]'
840 840
                           , exclusivelock  = " .($options['scope'] === "exclusive" ? "1" : "0")
841
-            ;
842
-        mysql_query($query);
843
-
844
-        return mysql_affected_rows() ? "200 OK" : "409 Conflict";
845
-    }
846
-
847
-    /**
848
-     * UNLOCK method handler
849
-     *
850
-     * @param  array  general parameter passing array
851
-     * @return bool   true on success
852
-     */
853
-    function UNLOCK(&$options)
854
-    {
855
-        $query = "DELETE FROM {$this->db_prefix}locks
841
+			;
842
+		mysql_query($query);
843
+
844
+		return mysql_affected_rows() ? "200 OK" : "409 Conflict";
845
+	}
846
+
847
+	/**
848
+	 * UNLOCK method handler
849
+	 *
850
+	 * @param  array  general parameter passing array
851
+	 * @return bool   true on success
852
+	 */
853
+	function UNLOCK(&$options)
854
+	{
855
+		$query = "DELETE FROM {$this->db_prefix}locks
856 856
                       WHERE path = '$options[path]'
857 857
                         AND token = '$options[token]'";
858
-        mysql_query($query);
859
-
860
-        return mysql_affected_rows() ? "204 No Content" : "409 Conflict";
861
-    }
862
-
863
-    /**
864
-     * checkLock() helper
865
-     *
866
-     * @param  string resource path to check for locks
867
-     * @return bool   true on success
868
-     */
869
-    function checkLock($path)
870
-    {
871
-        $result = false;
872
-
873
-        $query = "SELECT owner, token, created, modified, expires, exclusivelock
858
+		mysql_query($query);
859
+
860
+		return mysql_affected_rows() ? "204 No Content" : "409 Conflict";
861
+	}
862
+
863
+	/**
864
+	 * checkLock() helper
865
+	 *
866
+	 * @param  string resource path to check for locks
867
+	 * @return bool   true on success
868
+	 */
869
+	function checkLock($path)
870
+	{
871
+		$result = false;
872
+
873
+		$query = "SELECT owner, token, created, modified, expires, exclusivelock
874 874
                   FROM {$this->db_prefix}locks
875 875
                  WHERE path = '$path'
876 876
                ";
877
-        $res = mysql_query($query);
878
-
879
-        if ($res) {
880
-            $row = mysql_fetch_array($res);
881
-            mysql_free_result($res);
882
-
883
-            if ($row) {
884
-                $result = array( "type"    => "write",
885
-                                 "scope"   => $row["exclusivelock"] ? "exclusive" : "shared",
886
-                                 "depth"   => 0,
887
-                                 "owner"   => $row['owner'],
888
-                                 "token"   => $row['token'],
889
-                                 "created" => $row['created'],
890
-                                 "modified" => $row['modified'],
891
-                                 "expires" => $row['expires']
892
-                                 );
893
-            }
894
-        }
895
-
896
-        return $result;
897
-    }
898
-
899
-
900
-    /**
901
-     * create database tables for property and lock storage
902
-     *
903
-     * @param  void
904
-     * @return bool   true on success
905
-     */
906
-    function create_database()
907
-    {
908
-        // TODO
909
-        return false;
910
-    }
877
+		$res = mysql_query($query);
878
+
879
+		if ($res) {
880
+			$row = mysql_fetch_array($res);
881
+			mysql_free_result($res);
882
+
883
+			if ($row) {
884
+				$result = array( "type"    => "write",
885
+								 "scope"   => $row["exclusivelock"] ? "exclusive" : "shared",
886
+								 "depth"   => 0,
887
+								 "owner"   => $row['owner'],
888
+								 "token"   => $row['token'],
889
+								 "created" => $row['created'],
890
+								 "modified" => $row['modified'],
891
+								 "expires" => $row['expires']
892
+								 );
893
+			}
894
+		}
895
+
896
+		return $result;
897
+	}
898
+
899
+
900
+	/**
901
+	 * create database tables for property and lock storage
902
+	 *
903
+	 * @param  void
904
+	 * @return bool   true on success
905
+	 */
906
+	function create_database()
907
+	{
908
+		// TODO
909
+		return false;
910
+	}
911 911
 }
912 912
 
913 913
 
Please login to merge, or discard this patch.
api/src/Contacts.php 1 patch
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -145,19 +145,19 @@  discard block
 block discarded – undo
145 145
 	var $categories;
146 146
 
147 147
 	/**
148
-	* Tracking changes
149
-	*
150
-	* @var Contacts\Tracking
151
-	*/
148
+	 * Tracking changes
149
+	 *
150
+	 * @var Contacts\Tracking
151
+	 */
152 152
 	protected $tracking;
153 153
 
154 154
 	/**
155
-	* Keep deleted addresses, or really delete them
156
-	* Set in Admin -> Addressbook -> Site Configuration
157
-	* ''=really delete, 'history'=keep, only admins delete, 'userpurge'=keep, users delete
158
- 	*
159
-	* @var string
160
- 	*/
155
+	 * Keep deleted addresses, or really delete them
156
+	 * Set in Admin -> Addressbook -> Site Configuration
157
+	 * ''=really delete, 'history'=keep, only admins delete, 'userpurge'=keep, users delete
158
+	 *
159
+	 * @var string
160
+	 */
161 161
 	protected $delete_history = '';
162 162
 
163 163
 	/**
@@ -786,13 +786,13 @@  discard block
 block discarded – undo
786 786
 	}
787 787
 
788 788
 	/**
789
-	* deletes contact in db
790
-	*
791
-	* @param mixed &$contact contact array with key id or (array of) id(s)
792
-	* @param boolean $deny_account_delete =true if true never allow to delete accounts
793
-	* @param int $check_etag =null
794
-	* @return boolean|int true on success or false on failiure, 0 if etag does not match
795
-	*/
789
+	 * deletes contact in db
790
+	 *
791
+	 * @param mixed &$contact contact array with key id or (array of) id(s)
792
+	 * @param boolean $deny_account_delete =true if true never allow to delete accounts
793
+	 * @param int $check_etag =null
794
+	 * @return boolean|int true on success or false on failiure, 0 if etag does not match
795
+	 */
796 796
 	function delete($contact,$deny_account_delete=true,$check_etag=null)
797 797
 	{
798 798
 		if (is_array($contact) && isset($contact['id']))
@@ -844,12 +844,12 @@  discard block
 block discarded – undo
844 844
 	}
845 845
 
846 846
 	/**
847
-	* saves contact to db
848
-	*
849
-	* @param array &$contact contact array from etemplate::exec
850
-	* @param boolean $ignore_acl =false should the acl be checked or not
851
-	* @return int/string/boolean id on success, false on failure, the error-message is in $this->error
852
-	*/
847
+	 * saves contact to db
848
+	 *
849
+	 * @param array &$contact contact array from etemplate::exec
850
+	 * @param boolean $ignore_acl =false should the acl be checked or not
851
+	 * @return int/string/boolean id on success, false on failure, the error-message is in $this->error
852
+	 */
853 853
 	function save(&$contact,$ignore_acl=false)
854 854
 	{
855 855
 		// remember if we add or update a entry
@@ -1069,12 +1069,12 @@  discard block
 block discarded – undo
1069 1069
 	}
1070 1070
 
1071 1071
 	/**
1072
-	* reads contacts matched by key and puts all cols in the data array
1073
-	*
1074
-	* @param int|string $contact_id
1075
-	* @param boolean $ignore_acl =false true: no acl check
1076
-	* @return array|boolean array with contact data, null if not found or false on no view perms
1077
-	*/
1072
+	 * reads contacts matched by key and puts all cols in the data array
1073
+	 *
1074
+	 * @param int|string $contact_id
1075
+	 * @param boolean $ignore_acl =false true: no acl check
1076
+	 * @return array|boolean array with contact data, null if not found or false on no view perms
1077
+	 */
1078 1078
 	function read($contact_id, $ignore_acl=false)
1079 1079
 	{
1080 1080
 		// get so_sql_cf to read private customfields too, if we ignore acl
Please login to merge, or discard this patch.
api/src/Header/Authenticate.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -255,20 +255,20 @@
 block discarded – undo
255 255
 	 */
256 256
 	static public function parse_digest($txt)
257 257
 	{
258
-	    // protect against missing data
259
-	    $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
260
-	    $data = array();
261
-	    $keys = implode('|', array_keys($needed_parts));
258
+		// protect against missing data
259
+		$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
260
+		$data = array();
261
+		$keys = implode('|', array_keys($needed_parts));
262 262
 
263 263
 		$matches = null;
264
-	    preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
264
+		preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
265 265
 
266
-	    foreach ($matches as $m)
267
-	    {
268
-	        $data[$m[1]] = $m[3] ? $m[3] : $m[4];
269
-	        unset($needed_parts[$m[1]]);
270
-	    }
271
-	    //error_log(__METHOD__."('$txt') returning ".array2string($needed_parts ? false : $data));
272
-	    return $needed_parts ? false : $data;
266
+		foreach ($matches as $m)
267
+		{
268
+			$data[$m[1]] = $m[3] ? $m[3] : $m[4];
269
+			unset($needed_parts[$m[1]]);
270
+		}
271
+		//error_log(__METHOD__."('$txt') returning ".array2string($needed_parts ? false : $data));
272
+		return $needed_parts ? false : $data;
273 273
 	}
274 274
 }
Please login to merge, or discard this patch.
api/src/Etemplate/Widget/Vfs.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -224,13 +224,13 @@
 block discarded – undo
224 224
 	}
225 225
 
226 226
 	/**
227
-	* Ajax callback to receive an incoming file
228
-	*
229
-	* The incoming file is automatically placed into the appropriate VFS location.
230
-	* If the entry is not yet created, the file information is stored into the widget's value.
231
-	* When the form is submitted, the information for all files uploaded is available in the returned
232
-	* $content array and the application should deal with the file.
233
-	*/
227
+	 * Ajax callback to receive an incoming file
228
+	 *
229
+	 * The incoming file is automatically placed into the appropriate VFS location.
230
+	 * If the entry is not yet created, the file information is stored into the widget's value.
231
+	 * When the form is submitted, the information for all files uploaded is available in the returned
232
+	 * $content array and the application should deal with the file.
233
+	 */
234 234
 	public static function store_file($path, $file)
235 235
 	{
236 236
 		$name = $_REQUEST['widget_id'];
Please login to merge, or discard this patch.
api/src/Etemplate/Widget/File.php 1 patch
Indentation   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -238,10 +238,10 @@
 block discarded – undo
238 238
 	}
239 239
 
240 240
 	/**
241
-	* Delete a directory RECURSIVELY
242
-	* @param string $dir - directory path
243
-	* @link http://php.net/manual/en/function.rmdir.php
244
-	*/
241
+	 * Delete a directory RECURSIVELY
242
+	 * @param string $dir - directory path
243
+	 * @link http://php.net/manual/en/function.rmdir.php
244
+	 */
245 245
 	private static function rrmdir($dir)
246 246
 	{
247 247
 		if (is_dir($dir))
Please login to merge, or discard this patch.
api/src/Etemplate/Widget/HistoryLog.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@
 block discarded – undo
33 33
 	 * while it uses select-account for owner in historylog (containing all users).
34 34
 	 *
35 35
 	 * @param string $cname
36
-	*/
36
+	 */
37 37
 	public function beforeSendToClient($cname)
38 38
 	{
39 39
 		$form_name = self::form_name($cname, $this->id);
Please login to merge, or discard this patch.
api/src/Etemplate/Widget.php 1 patch
Indentation   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -903,12 +903,12 @@
 block discarded – undo
903 903
 	}
904 904
 
905 905
 	/**
906
-	* Check if we have not ignored validation errors
907
-	*
908
-	* @param string $ignore_validation ='' if not empty regular expression for validation-errors to ignore
909
-	* @param string $cname =null name-prefix, which need to be ignored, default self::$name_vars
910
-	* @return boolean true if there are not ignored validation errors, false otherwise
911
-	*/
906
+	 * Check if we have not ignored validation errors
907
+	 *
908
+	 * @param string $ignore_validation ='' if not empty regular expression for validation-errors to ignore
909
+	 * @param string $cname =null name-prefix, which need to be ignored, default self::$name_vars
910
+	 * @return boolean true if there are not ignored validation errors, false otherwise
911
+	 */
912 912
 	public static function validation_errors($ignore_validation='',$cname='')
913 913
 	{
914 914
 		// not yet used: if (is_null($cname)) $cname = self::$name_vars;
Please login to merge, or discard this patch.
api/src/Etemplate/WidgetBrowser.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -32,7 +32,7 @@
 block discarded – undo
32 32
 	{
33 33
 		$GLOBALS['egw_info']['flags']['currentapp'] = 'etemplate';
34 34
 		$GLOBALS['egw_info']['flags']['app_header'] = 'et2 Widgets';
35
-                //'js_link_registry'      => True,
35
+				//'js_link_registry'      => True,
36 36
 
37 37
 		// Widget browser code
38 38
 		Api\Framework::includeJS('/api/js/etemplate/widget_browser.js');
Please login to merge, or discard this patch.
api/src/Accounts/Ads.php 1 patch
Indentation   +174 added lines, -174 removed lines patch added patch discarded remove patch
@@ -807,11 +807,11 @@  discard block
 block discarded – undo
807 807
 	}
808 808
 
809 809
 	/**
810
-	* Add seconds between 1601-01-01 and 1970-01-01 and multiply by 10000000
811
-	*
812
-	* @param long $unixTime
813
-	* @return long windowsTime
814
-	*/
810
+	 * Add seconds between 1601-01-01 and 1970-01-01 and multiply by 10000000
811
+	 *
812
+	 * @param long $unixTime
813
+	 * @return long windowsTime
814
+	 */
815 815
 	public static function convertUnixTimeToWindowsTime($unixTime)
816 816
 	{
817 817
 		return ($unixTime + 11644477200) * 10000000;
@@ -1263,10 +1263,10 @@  discard block
 block discarded – undo
1263 1263
 	}
1264 1264
 
1265 1265
 	/**
1266
-    * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
1267
-    *
1268
-    * Extended to use mbstring to convert from arbitrary charset to utf-8
1269
-	*/
1266
+	 * Convert 8bit characters e.g. accented characters to UTF8 encoded characters
1267
+	 *
1268
+	 * Extended to use mbstring to convert from arbitrary charset to utf-8
1269
+	 */
1270 1270
 	protected function encode8Bit(&$item, $key)
1271 1271
 	{
1272 1272
 		if ($this->charset != 'utf-8' && $key != 'password')
@@ -1294,29 +1294,29 @@  discard block
 block discarded – undo
1294 1294
 		return $this->userClass;
1295 1295
 	}
1296 1296
 
1297
-    /**
1298
-    * Get the group class interface
1299
-    *
1300
-    * @return adLDAPGroups
1301
-    */
1302
-    public function group() {
1303
-        if (!$this->groupClass) {
1304
-            $this->groupClass = new adLDAPGroups($this);
1305
-        }
1306
-        return $this->groupClass;
1307
-    }
1308
-
1309
-    /**
1310
-    * Get the utils class interface
1311
-    *
1312
-    * @return adLDAPUtils
1313
-    */
1314
-    public function utilities() {
1315
-        if (!$this->utilClass) {
1316
-            $this->utilClass = new adLDAPUtils($this);
1317
-        }
1318
-        return $this->utilClass;
1319
-    }
1297
+	/**
1298
+	 * Get the group class interface
1299
+	 *
1300
+	 * @return adLDAPGroups
1301
+	 */
1302
+	public function group() {
1303
+		if (!$this->groupClass) {
1304
+			$this->groupClass = new adLDAPGroups($this);
1305
+		}
1306
+		return $this->groupClass;
1307
+	}
1308
+
1309
+	/**
1310
+	 * Get the utils class interface
1311
+	 *
1312
+	 * @return adLDAPUtils
1313
+	 */
1314
+	public function utilities() {
1315
+		if (!$this->utilClass) {
1316
+			$this->utilClass = new adLDAPUtils($this);
1317
+		}
1318
+		return $this->utilClass;
1319
+	}
1320 1320
 }
1321 1321
 
1322 1322
 /**
@@ -1405,45 +1405,45 @@  discard block
 block discarded – undo
1405 1405
 		return true;
1406 1406
 	}
1407 1407
 
1408
-    /**
1409
-    * Encode a password for transmission over LDAP
1410
-    *
1411
-    * Extended to use mbstring to convert from arbitrary charset to UTF-16LE
1412
-    *
1413
-    * @param string $password The password to encode
1414
-    * @return string
1415
-    */
1416
-    public function encodePassword($password)
1417
-    {
1418
-        $password="\"".$password."\"";
1419
-        if (function_exists('mb_convert_encoding'))
1420
-        {
1421
-            return mb_convert_encoding($password, 'UTF-16LE', $this->adldap->charset);
1422
-        }
1423
-        $encoded="";
1424
-        for ($i=0; $i <strlen($password); $i++){ $encoded.="{$password{$i}}\000"; }
1425
-        return $encoded;
1426
-    }
1427
-
1428
-    /**
1429
-     * Set a password
1430
-     *
1431
-     * Requires "Reset password" priviledges from bind user!
1432
-     *
1408
+	/**
1409
+	 * Encode a password for transmission over LDAP
1410
+	 *
1411
+	 * Extended to use mbstring to convert from arbitrary charset to UTF-16LE
1412
+	 *
1413
+	 * @param string $password The password to encode
1414
+	 * @return string
1415
+	 */
1416
+	public function encodePassword($password)
1417
+	{
1418
+		$password="\"".$password."\"";
1419
+		if (function_exists('mb_convert_encoding'))
1420
+		{
1421
+			return mb_convert_encoding($password, 'UTF-16LE', $this->adldap->charset);
1422
+		}
1423
+		$encoded="";
1424
+		for ($i=0; $i <strlen($password); $i++){ $encoded.="{$password{$i}}\000"; }
1425
+		return $encoded;
1426
+	}
1427
+
1428
+	/**
1429
+	 * Set a password
1430
+	 *
1431
+	 * Requires "Reset password" priviledges from bind user!
1432
+	 *
1433 1433
 	 * We can NOT set password with ldap_add or ldap_modify, it needs ldap_mod_replace, at least under Win2008r2!
1434 1434
 	 *
1435
-     * @param string $dn
1436
-     * @param string $password
1437
-     * @return boolean
1438
-     */
1439
-    public function setPassword($dn, $password)
1440
-    {
1441
-    	$result = ldap_mod_replace($ds=$this->adldap->getLdapConnection(), $dn, array(
1442
-    		'unicodePwd' => $this->encodePassword($password),
1443
-    	));
1444
-    	if (!$result) error_log(__METHOD__."('$dn', '$password') ldap_mod_replace($ds, '$dn', \$password) returned FALSE: ".ldap_error($ds));
1445
-    	return $result;
1446
-    }
1435
+	 * @param string $dn
1436
+	 * @param string $password
1437
+	 * @return boolean
1438
+	 */
1439
+	public function setPassword($dn, $password)
1440
+	{
1441
+		$result = ldap_mod_replace($ds=$this->adldap->getLdapConnection(), $dn, array(
1442
+			'unicodePwd' => $this->encodePassword($password),
1443
+		));
1444
+		if (!$result) error_log(__METHOD__."('$dn', '$password') ldap_mod_replace($ds, '$dn', \$password) returned FALSE: ".ldap_error($ds));
1445
+		return $result;
1446
+	}
1447 1447
 
1448 1448
 	/**
1449 1449
 	 * Check if we can to a real password change, not just a password reset
@@ -1457,30 +1457,30 @@  discard block
 block discarded – undo
1457 1457
 		return function_exists('ldap_modify_batch');
1458 1458
 	}
1459 1459
 
1460
-    /**
1461
-    * Set the password of a user - This must be performed over SSL
1462
-    *
1463
-    * @param string $username The username to modify
1464
-    * @param string $password The new password
1465
-    * @param bool $isGUID Is the username passed a GUID or a samAccountName
1466
-	* @param string $old_password old password for password change, if supported
1467
-    * @return bool
1468
-    */
1469
-    public function password($username, $password, $isGUID = false, $old_password=null)
1470
-    {
1471
-        if ($username === NULL) { return false; }
1472
-        if ($password === NULL) { return false; }
1473
-        if (!$this->adldap->getLdapBind()) { return false; }
1474
-        if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) {
1475
-            throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
1476
-        }
1477
-
1478
-        $userDn = $this->dn($username, $isGUID);
1479
-        if ($userDn === false) {
1480
-            return false;
1481
-        }
1482
-
1483
-        $add=array();
1460
+	/**
1461
+	 * Set the password of a user - This must be performed over SSL
1462
+	 *
1463
+	 * @param string $username The username to modify
1464
+	 * @param string $password The new password
1465
+	 * @param bool $isGUID Is the username passed a GUID or a samAccountName
1466
+	 * @param string $old_password old password for password change, if supported
1467
+	 * @return bool
1468
+	 */
1469
+	public function password($username, $password, $isGUID = false, $old_password=null)
1470
+	{
1471
+		if ($username === NULL) { return false; }
1472
+		if ($password === NULL) { return false; }
1473
+		if (!$this->adldap->getLdapBind()) { return false; }
1474
+		if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) {
1475
+			throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.');
1476
+		}
1477
+
1478
+		$userDn = $this->dn($username, $isGUID);
1479
+		if ($userDn === false) {
1480
+			return false;
1481
+		}
1482
+
1483
+		$add=array();
1484 1484
 
1485 1485
 		if (empty($old_password) || !function_exists('ldap_modify_batch')) {
1486 1486
 			$add["unicodePwd"][0] = $this->encodePassword($password);
@@ -1502,76 +1502,76 @@  discard block
 block discarded – undo
1502 1502
 			);
1503 1503
 			$result = ldap_modify_batch($this->adldap->getLdapConnection(), $userDn, $mods);
1504 1504
 		}
1505
-        if ($result === false){
1506
-            $err = ldap_errno($this->adldap->getLdapConnection());
1507
-            if ($err) {
1508
-                $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.';
1509
-                if($err == 53) {
1510
-                    $msg .= ' Your password might not match the password policy.';
1511
-                }
1512
-                throw new adLDAPException($msg);
1513
-            }
1514
-            else {
1515
-                return false;
1516
-            }
1517
-        }
1518
-
1519
-        return true;
1520
-    }
1521
-
1522
-    /**
1523
-    * Modify a user
1524
-    *
1525
-    * @param string $username The username to query
1526
-    * @param array $attributes The attributes to modify.  Note if you set the enabled attribute you must not specify any other attributes
1527
-    * @param bool $isGUID Is the username passed a GUID or a samAccountName
1528
-    * @return bool
1529
-    */
1530
-    public function modify($username, $attributes, $isGUID = false)
1531
-    {
1532
-        if ($username === NULL) { return "Missing compulsory field [username]"; }
1533
-        if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) {
1534
-            throw new adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.');
1535
-        }
1536
-
1537
-        // Find the dn of the user
1538
-        $userDn = $this->dn($username, $isGUID);
1539
-        if ($userDn === false) {
1540
-            return false;
1541
-        }
1542
-
1543
-        // Translate the update to the LDAP schema
1544
-        $mod = $this->adldap->adldap_schema($attributes);
1545
-
1546
-        // Check to see if this is an enabled status update
1547
-        if (!$mod && !array_key_exists("enabled", $attributes)){
1548
-            return false;
1549
-        }
1550
-
1551
-        // Set the account control attribute (only if specified)
1552
-        if (array_key_exists("enabled", $attributes)){
1553
-            if ($attributes["enabled"]){
1554
-                $controlOptions = array("NORMAL_ACCOUNT");
1555
-            }
1556
-            else {
1557
-                $controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE");
1558
-            }
1559
-            $mod["userAccountControl"][0] = $this->accountControl($controlOptions);
1560
-        }
1505
+		if ($result === false){
1506
+			$err = ldap_errno($this->adldap->getLdapConnection());
1507
+			if ($err) {
1508
+				$msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.';
1509
+				if($err == 53) {
1510
+					$msg .= ' Your password might not match the password policy.';
1511
+				}
1512
+				throw new adLDAPException($msg);
1513
+			}
1514
+			else {
1515
+				return false;
1516
+			}
1517
+		}
1518
+
1519
+		return true;
1520
+	}
1521
+
1522
+	/**
1523
+	 * Modify a user
1524
+	 *
1525
+	 * @param string $username The username to query
1526
+	 * @param array $attributes The attributes to modify.  Note if you set the enabled attribute you must not specify any other attributes
1527
+	 * @param bool $isGUID Is the username passed a GUID or a samAccountName
1528
+	 * @return bool
1529
+	 */
1530
+	public function modify($username, $attributes, $isGUID = false)
1531
+	{
1532
+		if ($username === NULL) { return "Missing compulsory field [username]"; }
1533
+		if (array_key_exists("password", $attributes) && !$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) {
1534
+			throw new adLDAPException('SSL/TLS must be configured on your webserver and enabled in the class to set passwords.');
1535
+		}
1536
+
1537
+		// Find the dn of the user
1538
+		$userDn = $this->dn($username, $isGUID);
1539
+		if ($userDn === false) {
1540
+			return false;
1541
+		}
1542
+
1543
+		// Translate the update to the LDAP schema
1544
+		$mod = $this->adldap->adldap_schema($attributes);
1545
+
1546
+		// Check to see if this is an enabled status update
1547
+		if (!$mod && !array_key_exists("enabled", $attributes)){
1548
+			return false;
1549
+		}
1550
+
1551
+		// Set the account control attribute (only if specified)
1552
+		if (array_key_exists("enabled", $attributes)){
1553
+			if ($attributes["enabled"]){
1554
+				$controlOptions = array("NORMAL_ACCOUNT");
1555
+			}
1556
+			else {
1557
+				$controlOptions = array("NORMAL_ACCOUNT", "ACCOUNTDISABLE");
1558
+			}
1559
+			$mod["userAccountControl"][0] = $this->accountControl($controlOptions);
1560
+		}
1561 1561
 		// we can NOT set password with ldap_add or ldap_modify, it needs ldap_mod_replace, at least under Win2008r2
1562 1562
 		unset($mod['unicodePwd']);
1563 1563
 
1564 1564
 		if ($mod)
1565 1565
 		{
1566
-	        // Do the update
1567
-	        $result = @ldap_modify($ds=$this->adldap->getLdapConnection(), $userDn, $mod);
1568
-	        if ($result == false) {
1566
+			// Do the update
1567
+			$result = @ldap_modify($ds=$this->adldap->getLdapConnection(), $userDn, $mod);
1568
+			if ($result == false) {
1569 1569
 				if (isset($mod['unicodePwd'])) $mod['unicodePwd'] = '***';
1570 1570
 				error_log(__METHOD__."(".array2string($attributes).") ldap_modify($ds, '$userDn', ".array2string($mod).") returned ".array2string($result)." ldap_error()=".ldap_error($ds));
1571
-	        	return false;
1572
-	        }
1571
+				return false;
1572
+			}
1573 1573
 		}
1574
-        if (array_key_exists("password",$attributes) && !$this->setPassword($userDn, $attributes['password']))
1574
+		if (array_key_exists("password",$attributes) && !$this->setPassword($userDn, $attributes['password']))
1575 1575
 		{
1576 1576
 			return false;
1577 1577
 		}
@@ -1636,23 +1636,23 @@  discard block
 block discarded – undo
1636 1636
 		return $this->adldap->encode8bit($item, $key);
1637 1637
 	}
1638 1638
 
1639
-    /**
1640
-    * Escape strings for the use in LDAP filters
1641
-    *
1642
-    * DEVELOPERS SHOULD BE DOING PROPER FILTERING IF THEY'RE ACCEPTING USER INPUT
1643
-    * Ported from Perl's Net::LDAP::Util escape_filter_value
1644
-    *
1645
-    * @param string $str The string the parse
1646
-    * @author Port by Andreas Gohr <[email protected]>
1647
-    * @return string
1648
-    */
1649
-    public function ldapSlashes($str){
1650
-        return preg_replace_callback(
1651
-      		'/([\x00-\x1F\*\(\)\\\\])/',
1652
-        	function ($matches) {
1653
-            	return "\\".join("", unpack("H2", $matches[1]));
1654
-        	},
1655
-        	$str
1656
-    	);
1657
-    }
1639
+	/**
1640
+	 * Escape strings for the use in LDAP filters
1641
+	 *
1642
+	 * DEVELOPERS SHOULD BE DOING PROPER FILTERING IF THEY'RE ACCEPTING USER INPUT
1643
+	 * Ported from Perl's Net::LDAP::Util escape_filter_value
1644
+	 *
1645
+	 * @param string $str The string the parse
1646
+	 * @author Port by Andreas Gohr <[email protected]>
1647
+	 * @return string
1648
+	 */
1649
+	public function ldapSlashes($str){
1650
+		return preg_replace_callback(
1651
+	  		'/([\x00-\x1F\*\(\)\\\\])/',
1652
+			function ($matches) {
1653
+				return "\\".join("", unpack("H2", $matches[1]));
1654
+			},
1655
+			$str
1656
+		);
1657
+	}
1658 1658
 }
Please login to merge, or discard this patch.