1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26: class Horde_Vfs_Sql extends Horde_Vfs_Base
27: {
28:
29: const FILE = 1;
30:
31:
32: const FOLDER = 2;
33:
34: 35: 36: 37: 38:
39: protected $_db = false;
40:
41: 42: 43: 44: 45:
46: public function __construct($params = array())
47: {
48: $params = array_merge(array('table' => 'horde_vfs'), $params);
49: parent::__construct($params);
50: $this->_db = $this->_params['db'];
51: unset($this->_params['db']);
52: }
53:
54: 55: 56: 57: 58: 59: 60: 61: 62:
63: public function size($path, $name)
64: {
65: $length_op = $this->_getFileSizeOp();
66: $sql = sprintf(
67: 'SELECT %s(vfs_data) FROM %s WHERE vfs_path = ? AND vfs_name = ?',
68: $length_op,
69: $this->_params['table']
70: );
71: $values = array($this->_convertPath($path), $name);
72: try {
73: $size = $this->_db->selectValue($sql, $values);
74: } catch (Horde_Db_Exception $e) {
75: throw new Horde_Vfs_Exception($e);
76: }
77:
78: if ($size === false) {
79: throw new Horde_Vfs_Exception(sprintf('Unable to check file size of "%s/%s".', $path, $name));
80: }
81:
82: return $size;
83: }
84:
85: 86: 87: 88: 89: 90: 91: 92: 93:
94: public function getFolderSize($path = null, $name = null)
95: {
96: try {
97: $where = null;
98: $params = array();
99: if (strlen($path)) {
100: $where = 'WHERE vfs_path = ? OR vfs_path LIKE ?';
101: $path = $this->_convertPath($path);
102: $params = array($path, $path . '/%');
103: }
104: $sql = sprintf('SELECT SUM(%s(vfs_data)) FROM %s %s',
105: $this->_getFileSizeOp(),
106: $this->_params['table'],
107: $where);
108: $size = $this->_db->selectValue($sql, $params);
109: } catch (Horde_Db_Exception $e) {
110: throw new Horde_Vfs_Exception($e);
111: }
112:
113: return (int)$size;
114: }
115:
116: 117: 118: 119: 120: 121: 122: 123: 124:
125: public function read($path, $name)
126: {
127: return $this->_readBlob($this->_params['table'], 'vfs_data', array(
128: 'vfs_path' => $this->_convertPath($path),
129: 'vfs_name' => $name
130: ));
131: }
132:
133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152:
153: public function readByteRange($path, $name, &$offset, $length, &$remaining)
154: {
155: $data = $this->_readBlob($this->_params['table'], 'vfs_data', array(
156: 'vfs_path' => $this->_convertPath($path),
157: 'vfs_name' => $name
158: ));
159:
160:
161:
162: $size = strlen ($data);
163: if ($length == -1 || (($length + $offset) > $size)) {
164: $length = $size - $offset;
165: }
166: if ($remaining < 0) {
167: $remaining = 0;
168: }
169:
170: $data = substr($data, $offset, $length);
171: $offset = $offset + $length;
172: $remaining = $size - $offset;
173:
174: return $data;
175: }
176:
177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187:
188: public function write($path, $name, $tmpFile, $autocreate = false)
189: {
190: 191:
192: return $this->writeData($path,
193: $name,
194: file_get_contents($tmpFile),
195: $autocreate);
196: }
197:
198: 199: 200: 201: 202: 203: 204: 205: 206: 207:
208: public function writeData($path, $name, $data, $autocreate = false)
209: {
210: $this->_checkQuotaWrite('string', $data);
211:
212: $path = $this->_convertPath($path);
213:
214:
215: try {
216: $sql = sprintf('SELECT vfs_id FROM %s WHERE vfs_path %s AND vfs_name = ?',
217: $this->_params['table'],
218: (!strlen($path) && $this->_db->dbsyntax == 'oci8') ? ' IS NULL' : ' = ' . $this->_db->quote($path));
219: $values = array($name);
220: $id = $this->_db->selectValue($sql, $values);
221: } catch (Horde_Db_Exception $e) {
222: throw new Horde_Vfs_Exception($e);
223: }
224:
225: if ($id) {
226: $this->_updateBlob($this->_params['table'], 'vfs_data', $data,
227: array('vfs_id' => $id),
228: array('vfs_modified' => time()));
229: return;
230: }
231:
232:
233: $dirs = explode('/', $path);
234: $path_name = array_pop($dirs);
235: $parent = implode('/', $dirs);
236: if (!$this->isFolder($parent, $path_name)) {
237: if (!$autocreate) {
238: throw new Horde_Vfs_Exception(sprintf('Folder "%s" does not exist', $path));
239: }
240:
241: $this->autocreatePath($path);
242: }
243:
244: return $this->_insertBlob($this->_params['table'], 'vfs_data', $data, array(
245: 'vfs_type' => self::FILE,
246: 'vfs_path' => $path,
247: 'vfs_name' => $name,
248: 'vfs_modified' => time(),
249: 'vfs_owner' => $this->_params['user']
250: ));
251: }
252:
253: 254: 255: 256: 257: 258: 259: 260:
261: public function deleteFile($path, $name)
262: {
263: $this->_checkQuotaDelete($path, $name);
264:
265: $path = $this->_convertPath($path);
266:
267: try {
268: $sql = sprintf('DELETE FROM %s WHERE vfs_type = ? AND vfs_path = ? AND vfs_name = ?',
269: $this->_params['table']);
270: $values = array(self::FILE, $path, $name);
271: $result = $this->_db->delete($sql, $values);
272: } catch (Horde_Db_Exception $e) {
273: throw new Horde_Vfs_Exception($e);
274: }
275:
276: if ($result == 0) {
277: throw new Horde_Vfs_Exception('Unable to delete VFS file.');
278: }
279:
280: return $result;
281: }
282:
283: 284: 285: 286: 287: 288: 289: 290: 291: 292:
293: public function rename($oldpath, $oldname, $newpath, $newname)
294: {
295: if (strpos($newpath, '/') === false) {
296: $parent = '';
297: $path = $newpath;
298: } else {
299: list($parent, $path) = explode('/', $newpath, 2);
300: }
301:
302: if (!$this->isFolder($parent, $path)) {
303: $this->autocreatePath($newpath);
304: }
305:
306: $oldpath = $this->_convertPath($oldpath);
307: $newpath = $this->_convertPath($newpath);
308:
309: $sql = 'UPDATE ' . $this->_params['table']
310: . ' SET vfs_path = ?, vfs_name = ?, vfs_modified = ? WHERE vfs_path = ? AND vfs_name = ?';
311: $values = array($newpath, $newname, time(), $oldpath, $oldname);
312:
313: try {
314: $result = $this->_db->update($sql, $values);
315: } catch (Horde_Db_Exception $e) {
316: throw new Horde_Vfs_Exception($e);
317: }
318:
319: if ($result == 0) {
320: throw new Horde_Vfs_Exception('Unable to rename VFS file.');
321: }
322:
323: $this->_recursiveRename($oldpath, $oldname, $newpath, $newname);
324: }
325:
326: 327: 328: 329: 330: 331: 332: 333:
334: public function createFolder($path, $name)
335: {
336: $sql = 'INSERT INTO ' . $this->_params['table']
337: . ' (vfs_type, vfs_path, vfs_name, vfs_modified, vfs_owner) VALUES (?, ?, ?, ?, ?)';
338: $values = array(self::FOLDER, $this->_convertPath($path), $name, time(), $this->_params['user']);
339:
340: try {
341: $this->_db->insert($sql, $values);
342: } catch (Horde_Db_Exception $e) {
343: throw new Horde_Vfs_Exception($e);
344: }
345: }
346:
347: 348: 349: 350: 351: 352: 353: 354: 355:
356: public function deleteFolder($path, $name, $recursive = false)
357: {
358: $path = $this->_convertPath($path);
359: $folderPath = $this->_getNativePath($path, $name);
360:
361:
362: if (!$recursive) {
363: $folderList = $this->listFolder($folderPath, null, true);
364: if (!empty($folderList)) {
365: throw new Horde_Vfs_Exception(sprintf('Unable to delete %s, the directory is not empty', $path . '/' . $name));
366: }
367: }
368:
369:
370: if (!is_null($this->_vfsSize)) {
371: $size = $this->getFolderSize($folderPath);
372: }
373:
374: 375:
376: try {
377: $sql = sprintf('DELETE FROM %s WHERE vfs_path %s',
378: $this->_params['table'],
379: (!strlen($folderPath) && $this->_db->dbsyntax == 'oci8') ? ' IS NULL' : ' LIKE ' . $this->_db->quote($this->_getNativePath($folderPath, '%')));
380: $this->_db->delete($sql);
381: } catch (Horde_Db_Exception $e) {
382: $this->_vfsSize = null;
383: throw new Horde_Vfs_Exception('Unable to delete VFS recursively: ' . $e->getMessage());
384: }
385:
386:
387: try {
388: $sql = sprintf('DELETE FROM %s WHERE vfs_path %s',
389: $this->_params['table'],
390: (!strlen($path) && $this->_db->dbsyntax == 'oci8') ? ' IS NULL' : ' = ' . $this->_db->quote($folderPath));
391: $this->_db->delete($sql);
392: } catch (Horde_Db_Exception $e) {
393: $this->_vfsSize = null;
394: throw new Horde_Vfs_Exception('Unable to delete VFS directory: ' . $e->getMessage());
395: }
396:
397:
398: try {
399: $sql = sprintf('DELETE FROM %s WHERE vfs_path %s AND vfs_name = ?',
400: $this->_params['table'],
401: (!strlen($path) && $this->_db->dbsyntax == 'oci8') ? ' IS NULL' : ' = ' . $this->_db->quote($path));
402: $values = array($name);
403: $this->_db->delete($sql, $values);
404: } catch (Horde_Db_Exception $e) {
405: $this->_vfsSize = null;
406: throw new Horde_Vfs_Exception('Unable to delete VFS directory: ' . $e->getMessage());
407: }
408:
409:
410: if (!is_null($this->_vfsSize)) {
411: $this->_vfsSize -= $size;
412: }
413: }
414:
415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426:
427: protected function _listFolder($path, $filter = null, $dotfiles = true,
428: $dironly = false)
429: {
430: $path = $this->_convertPath($path);
431:
432: try {
433:
434: if (!strlen($path) &&
435: ($this->_db->adapterName() == 'Oracle' ||
436: $this->_db->adapterName() == 'PDO_Oracle')) {
437: $where = 'vfs_path IS NULL';
438: } else {
439: $where = 'vfs_path = ' . $this->_db->quote($path);
440: }
441:
442: $length_op = $this->_getFileSizeOp();
443: $sql = sprintf('SELECT vfs_name, vfs_type, %s(vfs_data) length, vfs_modified, vfs_owner FROM %s WHERE %s',
444: $length_op,
445: $this->_params['table'],
446: $where);
447: $fileList = $this->_db->select($sql);
448: } catch (Horde_Db_Exception $e) {
449: throw new Horde_Vfs_Exception($e);
450: }
451:
452: $files = array();
453: foreach ($fileList as $line) {
454:
455: if (!$dotfiles && substr($line['vfs_name'], 0, 1) == '.') {
456: continue;
457: }
458:
459: $file['name'] = $line['vfs_name'];
460:
461: if ($line['vfs_type'] == self::FILE) {
462: $name = explode('.', $line['vfs_name']);
463:
464: if (count($name) == 1) {
465: $file['type'] = '**none';
466: } else {
467: $file['type'] = Horde_String::lower($name[count($name) - 1]);
468: }
469:
470: $file['size'] = $line['length'];
471: } elseif ($line['vfs_type'] == self::FOLDER) {
472: $file['type'] = '**dir';
473: $file['size'] = -1;
474: }
475:
476: $file['date'] = $line['vfs_modified'];
477: $file['owner'] = $line['vfs_owner'];
478: $file['perms'] = '';
479: $file['group'] = '';
480:
481:
482: if ($this->_filterMatch($filter, $file['name'])) {
483: unset($file);
484: continue;
485: }
486: if ($dironly && $file['type'] !== '**dir') {
487: unset($file);
488: continue;
489: }
490:
491: $files[$file['name']] = $file;
492: unset($file);
493: }
494:
495: return $files;
496: }
497:
498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509:
510: public function listFolders($path = '', $filter = array(),
511: $dotfolders = true)
512: {
513: $path = $this->_convertPath($path);
514:
515: $sql = 'SELECT vfs_name, vfs_path FROM ' . $this->_params['table']
516: . ' WHERE vfs_path = ? AND vfs_type = ?';
517: $values = array($path, self::FOLDER);
518:
519: try {
520: $folderList = $this->_db->select($sql, $values);
521: } catch (Horde_Db_Exception $e) {
522: throw new Horde_Vfs_Exception($e);
523: }
524:
525: $folders = array();
526: foreach ($folderList as $line) {
527:
528: if (!$dotfolders && substr($line['vfs_name'], 0, 1) == '.') {
529: continue;
530: }
531:
532: $folder['val'] = $this->_getNativePath($line['vfs_path'], $line['vfs_name']);
533: $folder['abbrev'] = '';
534: $folder['label'] = '';
535:
536: $count = substr_count($folder['val'], '/');
537:
538: $x = 0;
539: while ($x < $count) {
540: $folder['abbrev'] .= ' ';
541: $folder['label'] .= ' ';
542: $x++;
543: }
544:
545: $folder['abbrev'] .= $line['vfs_name'];
546: $folder['label'] .= $line['vfs_name'];
547:
548: $strlen = Horde_String::length($folder['label']);
549: if ($strlen > 26) {
550: $folder['abbrev'] = substr($folder['label'], 0, ($count * 4));
551: $length = (29 - ($count * 4)) / 2;
552: $folder['abbrev'] .= substr($folder['label'], ($count * 4), $length);
553: $folder['abbrev'] .= '...';
554: $folder['abbrev'] .= substr($folder['label'], -1 * $length, $length);
555: }
556:
557: $found = false;
558: foreach ($filter as $fltr) {
559: if ($folder['val'] == $fltr) {
560: $found = true;
561: }
562: }
563:
564: if (!$found) {
565: $folders[$folder['val']] = $folder;
566: }
567: }
568:
569: ksort($folders);
570: return $folders;
571: }
572:
573: 574: 575: 576: 577: 578: 579: 580: 581:
582: public function gc($path, $secs = 345600)
583: {
584: $sql = 'DELETE FROM ' . $this->_params['table']
585: . ' WHERE vfs_type = ? AND vfs_modified < ? AND (vfs_path = ? OR vfs_path LIKE ?)';
586: $values = array(
587: self::FILE,
588: time() - $secs,
589: $this->_convertPath($path),
590: $this->_convertPath($path) . '/%'
591: );
592:
593: try {
594: $this->_db->delete($sql, $values);
595: } catch (Horde_Db_Exception $e) {
596: throw new Horde_Vfs_Exception($e);
597: }
598: }
599:
600: 601: 602: 603: 604: 605: 606: 607:
608: protected function _recursiveRename($oldpath, $oldname, $newpath, $newname)
609: {
610: $oldpath = $this->_convertPath($oldpath);
611: $newpath = $this->_convertPath($newpath);
612:
613: $sql = 'SELECT vfs_name FROM ' . $this->_params['table']
614: . ' WHERE vfs_type = ? AND vfs_path = ?';
615: $values = array(self::FOLDER, $this->_getNativePath($oldpath, $oldname));
616:
617: try {
618: $folderList = $this->_db->selectValues($sql, $values);
619: } catch (Horde_Db_Exception $e) {
620: throw new Horde_Vfs_Exception($e);
621: }
622:
623: foreach ($folderList as $folder) {
624: $this->_recursiveRename($this->_getNativePath($oldpath, $oldname), $folder, $this->_getNativePath($newpath, $newname), $folder);
625: }
626:
627: $sql = 'UPDATE ' . $this->_params['table']
628: . ' SET vfs_path = ? WHERE vfs_path = ?';
629: $values = array($this->_getNativePath($newpath, $newname), $this->_getNativePath($oldpath, $oldname));
630:
631: try {
632: $this->_db->update($sql, $values);
633: } catch (Horde_Db_Exception $e) {
634: throw new Horde_Vfs_Exception($e);
635: }
636: }
637:
638: 639: 640: 641: 642: 643: 644: 645: 646:
647: protected function _getNativePath($path, $name)
648: {
649: if (!strlen($path)) {
650: return $name;
651: }
652:
653: if (isset($this->_params['home']) &&
654: preg_match('|^~/?(.*)$|', $path, $matches)) {
655: $path = $this->_params['home'] . '/' . $matches[1];
656: }
657:
658: return $path . '/' . $name;
659: }
660:
661: 662: 663: 664: 665: 666: 667: 668: 669: 670:
671: protected function _readBlob($table, $field, $criteria)
672: {
673: if (!count($criteria)) {
674: throw new Horde_Vfs_Exception('You must specify the fetch criteria');
675: }
676:
677: $where = '';
678: foreach ($criteria as $key => $value) {
679: if (!empty($where)) {
680: $where .= ' AND ';
681: }
682: $where .= $key . ' = ' . $this->_db->quote($value);
683: }
684:
685: $sql = sprintf('SELECT %s FROM %s WHERE %s',
686: $field, $table, $where);
687:
688: try {
689: $result = $this->_db->selectValue($sql);
690: $columns = $this->_db->columns($table);
691: } catch (Horde_Db_Exception $e) {
692: throw new Horde_Vfs_Exception($e);
693: }
694:
695: if ($result === false) {
696: throw new Horde_Vfs_Exception('Unable to load SQL data.');
697: }
698:
699: return $columns[$field]->binaryToString($result);
700: }
701:
702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712:
713: protected function _insertBlob($table, $field, $data, $attributes)
714: {
715: $fields = $values = array();
716: foreach ($attributes as $key => $value) {
717: $fields[] = $key;
718: $values[] = $value;
719: }
720: $values[] = new Horde_Db_Value_Binary($data);
721:
722: $query = sprintf('INSERT INTO %s (%s, %s) VALUES (%s)',
723: $table,
724: implode(', ', $fields),
725: $field,
726: '?' . str_repeat(', ?', count($fields)));
727:
728:
729: try {
730: $this->_db->insert($query, $values);
731: } catch (Horde_Db_Exception $e) {
732: throw new Horde_Vfs_Exception($e);
733: }
734: }
735:
736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747:
748: protected function _updateBlob($table, $field, $data, $where, $alsoupdate)
749: {
750: $updatestring = '';
751: $values = array();
752: foreach ($alsoupdate as $key => $value) {
753: $updatestring .= $key . ' = ?, ';
754: $values[] = $value;
755: }
756: $updatestring .= $field . ' = ?';
757: $values[] = new Horde_Db_Value_Binary($data);
758:
759: $wherestring = '';
760: foreach ($where as $key => $value) {
761: if (!empty($wherestring)) {
762: $wherestring .= ' AND ';
763: }
764: $wherestring .= $key . ' = ?';
765: $values[] = $value;
766: }
767:
768: $query = sprintf('UPDATE %s SET %s WHERE %s',
769: $table,
770: $updatestring,
771: $wherestring);
772:
773:
774: try {
775: $this->_db->update($query, $values);
776: } catch (Horde_Db_Exception $e) {
777: throw new Horde_Vfs_Exception($e);
778: }
779: }
780:
781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791:
792: protected function _convertPath($path)
793: {
794: return trim($path, '/');
795: }
796:
797: 798: 799: 800: 801:
802: protected function _getFileSizeOp()
803: {
804: switch ($this->_db->adapterName()) {
805: case 'Oracle':
806: case 'PDO_Oracle':
807: return 'LENGTHB';
808:
809: case 'PostgreSQL':
810: case 'PDO_PostgreSQL':
811: return 'OCTET_LENGTH';
812:
813: default:
814: return 'LENGTH';
815: }
816: }
817:
818: 819: 820: 821: 822: 823: 824: 825:
826: public function isFolder($path, $name)
827: {
828: return (($path == '') && ($name == ''))
829:
830: ? true
831: : parent::isFolder($path, $name);
832: }
833:
834: }
835: