1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13: class Horde_Vfs_File extends Horde_Vfs_Base
14: {
15: 16: 17: 18: 19:
20: protected $_permissions = array(
21: 'owner' => array(
22: 'read' => true,
23: 'write' => true,
24: 'execute' => true
25: ),
26: 'group' => array(
27: 'read' => true,
28: 'write' => true,
29: 'execute' => true
30: ),
31: 'all' => array(
32: 'read' => true,
33: 'write' => true,
34: 'execute' => true
35: )
36: );
37:
38: 39: 40: 41: 42: 43: 44: 45: 46:
47: public function __construct($params = array())
48: {
49: parent::__construct($params);
50:
51: if (!empty($this->_params['vfsroot']) &&
52: ((substr($this->_params['vfsroot'], -1) == '/') ||
53: (substr($this->_params['vfsroot'], -1) == '\\'))) {
54: $this->_params['vfsroot'] = substr($this->_params['vfsroot'], 0, strlen($this->_params['vfsroot']) - 1);
55: }
56: }
57:
58: 59: 60: 61: 62: 63: 64: 65: 66:
67: public function size($path, $name)
68: {
69: if (($size = @filesize($this->_getNativePath($path, $name))) === false) {
70: throw new Horde_Vfs_Exception(sprintf('Unable to check file size of "%s/%s".'), $path, $name);
71: }
72:
73: return $size;
74: }
75:
76: 77: 78: 79: 80: 81: 82: 83: 84:
85: public function read($path, $name)
86: {
87: if (($data = @file_get_contents($this->_getNativePath($path, $name))) === false) {
88: throw new Horde_Vfs_Exception('Unable to open VFS file.');
89: }
90:
91: return $data;
92: }
93:
94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105:
106: public function readFile($path, $name)
107: {
108: return $this->_getNativePath($path, $name);
109: }
110:
111: 112: 113: 114: 115: 116: 117: 118: 119:
120: public function readStream($path, $name)
121: {
122: $mode = OS_WINDOWS ? 'rb' : 'r';
123: $stream = @fopen($this->_getNativePath($path, $name), $mode);
124: if (!is_resource($stream)) {
125: throw new Horde_Vfs_Exception('Unable to open VFS file.');
126: }
127:
128: return $stream;
129: }
130:
131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150:
151: public function readByteRange($path, $name, &$offset, $length, &$remaining)
152: {
153: if ($offset < 0) {
154: throw new Horde_Vfs_Exception(sprintf('Wrong offset %d while reading a VFS file.', $offset));
155: }
156:
157:
158:
159: $file = $this->_getNativePath($path, $name);
160: $size = filesize ($file);
161: if (($length == -1) || (($length + $offset) > $size)) {
162: $length = $size - $offset;
163: }
164: if ($remaining < 0) {
165: $remaining = 0;
166: }
167:
168: $fp = @fopen($file, 'rb');
169: if (!$fp) {
170: throw new Horde_Vfs_Exception('Unable to open VFS file.');
171: }
172: fseek($fp, $offset);
173: $data = fread($fp, $length);
174: $offset = ftell($fp);
175: $remaining = $size - $offset;
176:
177: fclose($fp);
178:
179: return $data;
180: }
181:
182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193:
194: public function write($path, $name, $tmpFile, $autocreate = false)
195: {
196: if (!@is_dir($this->_getNativePath($path))) {
197: if ($autocreate) {
198: $this->autocreatePath($path);
199: } else {
200: throw new Horde_Vfs_Exception('VFS directory does not exist.');
201: }
202: }
203:
204: $this->_checkQuotaWrite('file', $tmpFile);
205:
206:
207:
208:
209:
210: if (!@copy($tmpFile, $this->_getNativePath($path, $name))) {
211: throw new Horde_Vfs_Exception('Unable to write VFS file (copy() failed).');
212: }
213: }
214:
215: 216: 217: 218: 219: 220: 221: 222: 223: 224:
225: public function move($path, $name, $dest, $autocreate = false)
226: {
227: $orig = $this->_getNativePath($path, $name);
228: if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) {
229: throw new Horde_Vfs_Exception('Cannot move file(s) - destination is within source.');
230: }
231:
232: if ($autocreate) {
233: $this->autocreatePath($dest);
234: }
235:
236: foreach ($this->listFolder($dest, false) as $file) {
237: if ($file['name'] == $name) {
238: throw new Horde_Vfs_Exception('Unable to move VFS file.');
239: }
240: }
241:
242: if (!@rename($orig, $this->_getNativePath($dest, $name))) {
243: throw new Horde_Vfs_Exception('Unable to move VFS file.');
244: }
245: }
246:
247: 248: 249: 250: 251: 252: 253: 254: 255: 256:
257: public function copy($path, $name, $dest, $autocreate = false)
258: {
259: $orig = $this->_getNativePath($path, $name);
260: if (preg_match('|^' . preg_quote($orig) . '/?$|', $dest)) {
261: throw new Horde_Vfs_Exception('Cannot copy file(s) - source and destination are the same.');
262: }
263:
264: if ($autocreate) {
265: $this->autocreatePath($dest);
266: }
267:
268: foreach ($this->listFolder($dest, false) as $file) {
269: if ($file['name'] == $name) {
270: throw new Horde_Vfs_Exception('Unable to copy VFS file.');
271: }
272: }
273:
274: $this->_checkQuotaWrite('file', $orig);
275:
276: if (!@copy($orig, $this->_getNativePath($dest, $name))) {
277: throw new Horde_Vfs_Exception('Unable to copy VFS file.');
278: }
279: }
280:
281: 282: 283: 284: 285: 286: 287: 288: 289: 290:
291: public function writeData($path, $name, $data, $autocreate = false)
292: {
293: if (!@is_dir($this->_getNativePath($path))) {
294: if ($autocreate) {
295: $this->autocreatePath($path);
296: } else {
297: throw new Horde_Vfs_Exception(sprintf('VFS directory %s does not exist.', $path));
298: }
299: }
300:
301:
302:
303: if (!strlen($data)) {
304: if (@touch($this->_getNativePath($path, $name))) {
305: return;
306: }
307: throw new Horde_Vfs_Exception('Unable to create empty VFS file.');
308: }
309:
310: $this->_checkQuotaWrite('string', $data);
311:
312:
313: if (!@file_put_contents($this->_getNativePath($path, $name), $data)) {
314: throw new Horde_Vfs_Exception('Unable to write VFS file data.');
315: }
316: }
317:
318: 319: 320: 321: 322: 323: 324: 325:
326: public function deleteFile($path, $name)
327: {
328: $this->_checkQuotaDelete($path, $name);
329:
330: if (!@unlink($this->_getNativePath($path, $name))) {
331: throw new Horde_Vfs_Exception('Unable to delete VFS file.');
332: }
333: }
334:
335: 336: 337: 338: 339: 340: 341: 342: 343:
344: public function deleteFolder($path, $name, $recursive = false)
345: {
346: if ($recursive) {
347: $this->emptyFolder($path . '/' . $name);
348: } else {
349: $list = $this->listFolder($path . '/' . $name);
350: if (count($list)) {
351: throw new Horde_Vfs_Exception(sprintf('Unable to delete %s, the directory is not empty', $path . '/' . $name));
352: }
353: }
354:
355: if (!@rmdir($this->_getNativePath($path, $name))) {
356: throw new Horde_Vfs_Exception('Unable to delete VFS directory.');
357: }
358: }
359:
360: 361: 362: 363: 364: 365: 366: 367:
368: public function createFolder($path, $name)
369: {
370: if (!@mkdir($this->_getNativePath($path, $name))) {
371: throw new Horde_Vfs_Exception('Unable to create VFS directory.');
372: }
373: }
374:
375: 376: 377: 378: 379: 380: 381: 382:
383: public function isFolder($path, $name)
384: {
385: return @is_dir($this->_getNativePath($path, $name));
386: }
387:
388: 389: 390: 391: 392: 393: 394: 395: 396:
397: public function changePermissions($path, $name, $permission)
398: {
399: if (!@chmod($this->_getNativePath($path, $name), base_convert($permission, 8, 10))) {
400: throw new Horde_Vfs_Exception(sprintf('Unable to change permission for VFS file %s/%s.', $path, $name));
401: }
402: }
403:
404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414:
415: protected function _listFolder($path, $filter = null, $dotfiles = true,
416: $dironly = false)
417: {
418: $files = array();
419: $path = $this->_getNativePath(isset($path) ? $path : '');
420:
421: if (!@is_dir($path)) {
422: throw new Horde_Vfs_Exception('Not a directory');
423: }
424:
425: if (!@chdir($path)) {
426: throw new Horde_Vfs_Exception('Unable to access VFS directory.');
427: }
428:
429: $d = dir($path);
430: while (($entry = $d->read()) !== false) {
431:
432: if ($entry == '.' || $entry == '..') {
433: continue;
434: }
435:
436:
437: if (!$dotfiles && substr($entry, 0, 1) == '.') {
438: continue;
439: }
440:
441:
442: $file['name'] = $entry;
443:
444:
445: $file['perms'] = $this->_getUnixPerms(fileperms($entry));
446:
447:
448: $file['owner'] = fileowner($entry);
449: if (function_exists('posix_getpwuid')) {
450: $owner = posix_getpwuid($file['owner']);
451: $file['owner'] = $owner['name'];
452: }
453:
454:
455: $file['group'] = filegroup($entry);
456: if (function_exists('posix_getgrgid') && (PHP_VERSION != '5.2.1')) {
457: $group = posix_getgrgid($file['group']);
458: $file['group'] = $group['name'];
459: }
460:
461:
462: $file['size'] = filesize($entry);
463:
464:
465: $file['date'] = filemtime($entry);
466:
467:
468: if (@is_dir($entry) && !is_link($entry)) {
469: $file['perms'] = 'd' . $file['perms'];
470: $file['type'] = '**dir';
471: $file['size'] = -1;
472: } elseif (is_link($entry)) {
473: $file['perms'] = 'l' . $file['perms'];
474: $file['type'] = '**sym';
475: $file['link'] = readlink($entry);
476: $file['linktype'] = '**none';
477: if (file_exists($file['link'])) {
478: if (is_dir($file['link'])) {
479: $file['linktype'] = '**dir';
480: } elseif (is_link($file['link'])) {
481: $file['linktype'] = '**sym';
482: } elseif (is_file($file['link'])) {
483: $ext = explode('.', $file['link']);
484: if (!(count($ext) == 1 || ($ext[0] === '' && count($ext) == 2))) {
485: $file['linktype'] = Horde_String::lower($ext[count($ext) - 1]);
486: }
487: }
488: } else {
489: $file['linktype'] = '**broken';
490: }
491: } elseif (is_file($entry)) {
492: $file['perms'] = '-' . $file['perms'];
493: $ext = explode('.', $entry);
494:
495: if (count($ext) == 1 || (substr($file['name'], 0, 1) === '.' && count($ext) == 2)) {
496: $file['type'] = '**none';
497: } else {
498: $file['type'] = Horde_String::lower($ext[count($ext) - 1]);
499: }
500: } else {
501: $file['type'] = '**none';
502: if ((fileperms($entry) & 0xC000) == 0xC000) {
503: $file['perms'] = 's' . $file['perms'];
504: } elseif ((fileperms($entry) & 0x6000) == 0x6000) {
505: $file['perms'] = 'b' . $file['perms'];
506: } elseif ((fileperms($entry) & 0x2000) == 0x2000) {
507: $file['perms'] = 'c' . $file['perms'];
508: } elseif ((fileperms($entry) & 0x1000) == 0x1000) {
509: $file['perms'] = 'p' . $file['perms'];
510: } else {
511: $file['perms'] = '?' . $file['perms'];
512: }
513: }
514:
515:
516: if ($this->_filterMatch($filter, $file['name'])) {
517: unset($file);
518: continue;
519: }
520: if ($dironly && $file['type'] !== '**dir') {
521: unset($file);
522: continue;
523: }
524:
525: $files[$file['name']] = $file;
526: unset($file);
527: }
528:
529: $d->close();
530:
531: return $files;
532: }
533:
534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544:
545: public function listFolders($path = '', $filter = null, $dotfolders = true)
546: {
547: $this->_connect();
548:
549: $folders = array();
550: $folders[dirname($path)] = array(
551: 'val' => dirname($path),
552: 'abbrev' => '..',
553: 'label' => '..'
554: );
555:
556: $folderList = $this->listFolder($path, null, $dotfolders, true);
557: foreach ($folderList as $name => $files) {
558: $folders[$name] = array(
559: 'val' => $path . '/' . $files['name'],
560: 'abbrev' => $files['name'],
561: 'label' => $path . '/' . $files['name']
562: );
563: }
564:
565: ksort($folders);
566:
567: return $folders;
568: }
569:
570: 571: 572: 573: 574: 575: 576:
577: protected function _getUnixPerms($perms)
578: {
579:
580: $owner['read'] = ($perms & 00400) ? 'r' : '-';
581: $owner['write'] = ($perms & 00200) ? 'w' : '-';
582: $owner['execute'] = ($perms & 00100) ? 'x' : '-';
583: $group['read'] = ($perms & 00040) ? 'r' : '-';
584: $group['write'] = ($perms & 00020) ? 'w' : '-';
585: $group['execute'] = ($perms & 00010) ? 'x' : '-';
586: $world['read'] = ($perms & 00004) ? 'r' : '-';
587: $world['write'] = ($perms & 00002) ? 'w' : '-';
588: $world['execute'] = ($perms & 00001) ? 'x' : '-';
589:
590:
591: if ($perms & 0x800) {
592: $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S';
593: }
594: if ($perms & 0x400) {
595: $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S';
596: }
597: if ($perms & 0x200) {
598: $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T';
599: }
600:
601: return $owner['read'] . $owner['write'] . $owner['execute'] .
602: $group['read'] . $group['write'] . $group['execute'] .
603: $world['read'] . $world['write'] . $world['execute'];
604: }
605:
606: 607: 608: 609: 610: 611: 612: 613: 614: 615:
616: public function rename($oldpath, $oldname, $newpath, $newname)
617: {
618: if (!@is_dir($this->_getNativePath($newpath))) {
619: $this->autocreatePath($newpath);
620: }
621:
622: if (!@rename($this->_getNativePath($oldpath, $oldname),
623: $this->_getNativePath($newpath, $newname))) {
624: throw new Horde_Vfs_Exception(sprintf('Unable to rename VFS file %s/%s.', $oldpath, $oldname));
625: }
626: }
627:
628: 629: 630: 631: 632: 633: 634: 635:
636: public function exists($path, $name)
637: {
638: return file_exists($this->_getNativePath($path, $name));
639: }
640:
641: 642: 643: 644: 645: 646: 647: 648: 649:
650: protected function _getNativePath($path = '', $name = '')
651: {
652: $name = basename($name);
653: if (strlen($name)) {
654: if ($name == '..') {
655: $name = '';
656: }
657: if (substr($name, 0, 1) != '/') {
658: $name = '/' . $name;
659: }
660: }
661:
662: if (strlen($path)) {
663: if (isset($this->_params['home']) &&
664: preg_match('|^~/?(.*)$|', $path, $matches)) {
665: $path = $this->_params['home'] . '/' . $matches[1];
666: }
667:
668: $path = str_replace('..', '', $path);
669: if (substr($path, 0, 1) == '/') {
670: return $this->_params['vfsroot'] . $path . $name;
671: } else {
672: return $this->_params['vfsroot'] . '/' . $path . $name;
673: }
674: }
675:
676: return $this->_params['vfsroot'] . $name;
677: }
678:
679: 680: 681: 682: 683:
684: protected function _connect()
685: {
686: if (!@is_dir($this->_params['vfsroot'])) {
687: @mkdir($this->_params['vfsroot']);
688: }
689:
690: if (!is_readable($this->_params['vfsroot'])) {
691: throw new Horde_Vfs_Exception('Unable to read the vfsroot directory.');
692: }
693: }
694:
695: }
696: