Overview

Packages

  • Vfs

Classes

  • Horde_Vfs
  • Horde_Vfs_Base
  • Horde_Vfs_Browser
  • Horde_Vfs_Exception
  • Horde_Vfs_File
  • Horde_Vfs_Ftp
  • Horde_Vfs_Gc
  • Horde_Vfs_Horde
  • Horde_Vfs_Kolab
  • Horde_Vfs_ListItem
  • Horde_Vfs_Musql
  • Horde_Vfs_Object
  • Horde_Vfs_Smb
  • Horde_Vfs_Sql
  • Horde_Vfs_SqlFile
  • Horde_Vfs_Ssh2
  • Horde_Vfs_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Multi User VFS implementation for Horde's database abstraction layer.
  4:  *
  5:  * Required values for $params:
  6:  * - db: A Horde_Db_Adapter object.
  7:  *
  8:  * Optional values:
  9:  * - table: (string) The name of the vfs table in 'database'. Defaults to
 10:  *          'horde_muvfs'.
 11:  *
 12:  * Known Issues:
 13:  * Delete is not recusive, so files and folders that used to be in a folder
 14:  * that gets deleted live forever in the database, or re-appear when the folder
 15:  * is recreated.
 16:  * Rename has the same issue, so files are lost if a folder is renamed.
 17:  *
 18:  * The table structure for the VFS can be created with the horde-db-migrate
 19:  * script from the Horde_Db package.
 20:  *
 21:  * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
 22:  *
 23:  * See the enclosed file COPYING for license information (LGPL). If you
 24:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 25:  *
 26:  * @author  Chuck Hagenbuch <chuck@horde.org>
 27:  * @author  Mike Cochrane <mike@graftonhall.co.nz>
 28:  * @package Vfs
 29:  */
 30: class Horde_Vfs_Musql extends Horde_Vfs_Sql
 31: {
 32:     /* Permission for read access. */
 33:     const FLAG_READ = 1;
 34: 
 35:     /* Permission for read access. */
 36:     const FLAG_WRITE = 2;
 37: 
 38:     /**
 39:      * List of permissions and if they can be changed in this VFS
 40:      *
 41:      * @var array
 42:      */
 43:     protected $_permissions = array(
 44:         'owner' => array(
 45:             'read' => false,
 46:             'write' => false,
 47:             'execute' => false
 48:         ),
 49:         'group' => array(
 50:             'read' => false,
 51:             'write' => false,
 52:             'execute' => false
 53:         ),
 54:         'all' => array(
 55:             'read' => true,
 56:             'write' => true,
 57:             'execute' => false
 58:         )
 59:     );
 60: 
 61:     /**
 62:      * Stores a file in the VFS from raw data.
 63:      *
 64:      * @param string $path         The path to store the file in.
 65:      * @param string $name         The filename to use.
 66:      * @param string $data         The file data.
 67:      * @param boolean $autocreate  Automatically create directories?
 68:      *
 69:      * @throws Horde_Vfs_Exception
 70:      */
 71:     public function writeData($path, $name, $data, $autocreate = false)
 72:     {
 73:         /* Make sure we have write access to this and all parent paths. */
 74:         if ($path != '') {
 75:             $paths = explode('/', $path);
 76:             $path_name = array_pop($paths);
 77:             if (!$this->isFolder(implode('/', $paths), $path_name)) {
 78:                 if (!$autocreate) {
 79:                     throw new Horde_Vfs_Exception(sprintf('Folder "%s" does not exist'), $path);
 80:                 } else {
 81:                     $this->autocreatePath($path);
 82:                 }
 83:             }
 84:             $paths[] = $path_name;
 85:             $previous = '';
 86: 
 87:             foreach ($paths as $thispath) {
 88:                 $sql = sprintf('SELECT vfs_owner, vfs_perms FROM %s
 89:                                 WHERE vfs_path = ? AND vfs_name= ?',
 90:                                $this->_params['table']);
 91:                 try {
 92:                     $results = $this->_db->selectAll($sql, array($previous, $thispath));
 93:                 } catch (Horde_Db_Exception $e) {
 94:                     throw new Horde_Vfs_Exception($e);
 95:                 }
 96:                 if (!is_array($results) || count($results) < 1) {
 97:                     throw new Horde_Vfs_Exception('Unable to create VFS file.');
 98:                 }
 99: 
100:                 $allowed = false;
101:                 foreach ($results as $result) {
102:                     if ($result['vfs_owner'] == $this->_params['user'] ||
103:                         $result['vfs_perm'] & self::FLAG_WRITE) {
104:                         $allowed = true;
105:                         break;
106:                     }
107:                 }
108: 
109:                 if (!$allowed) {
110:                     throw new Horde_Vfs_Exception('Access denied creating VFS file.');
111:                 }
112: 
113:                 $previous = $thispath;
114:             }
115:         }
116: 
117:         return parent::writeData($path, $name, $data, $autocreate);
118:     }
119: 
120:     /**
121:      * Deletes a file from the VFS.
122:      *
123:      * @param string $path  The path to store the file in.
124:      * @param string $name  The filename to use.
125:      *
126:      * @throws Horde_Vfs_Exception
127:      */
128:     public function deleteFile($path, $name)
129:     {
130:         $sql = sprintf('SELECT vfs_id, vfs_owner, vfs_perms FROM %s
131:                         WHERE vfs_path = ? AND vfs_name= ? AND vfs_type = ?',
132:                        $this->_params['table']);
133:         try {
134:             $fileList = $this->_db->selectAll($sql, array($path, $name, self::FILE));
135:         } catch (Horde_Db_Exception $e) {
136:             throw new Horde_Vfs_Exception($e);
137:         }
138:         if (!is_array($fileList) || count($fileList) < 1) {
139:             throw new Horde_Vfs_Exception('Unable to delete VFS file.');
140:         }
141: 
142:         /* There may be one or more files with the same name but the user may
143:          * not have read access to them, so doesn't see them. So we have to
144:          * delete the one they have access to. */
145:         foreach ($fileList as $file) {
146:             if ($file['vfs_owner'] == $this->_params['user'] ||
147:                 $file['vfs_perms'] & self::FLAG_WRITE) {
148:                 $sql = sprintf('DELETE FROM %s WHERE vfs_id = ?',
149:                                $this->_params['table']);
150:                 try {
151:                     $result = $this->_db->delete($sql, array($file['vfs_id']));
152:                 } catch (Horde_Db_Exception $e) {
153:                     throw new Horde_Vfs_Exception($e);
154:                 }
155:                 if ($result == 0) {
156:                     throw new Horde_Vfs_Exception('Unable to delete VFS file.');
157:                 }
158:                 return $result;
159:             }
160:         }
161: 
162:         // FIXME: 'Access Denied deleting file %s/%s'
163:         throw new Horde_Vfs_Exception('Unable to delete VFS file.');
164:     }
165: 
166:     /**
167:      * Renames a file or folder in the VFS.
168:      *
169:      * @param string $oldpath  The old path to the file.
170:      * @param string $oldname  The old filename.
171:      * @param string $newpath  The new path of the file.
172:      * @param string $newname  The new filename.
173:      *
174:      * @throws Horde_Vfs_Exception
175:      */
176:     public function rename($oldpath, $oldname, $newpath, $newname)
177:     {
178:         $sql = sprintf('SELECT vfs_id, vfs_owner, vfs_perms FROM %s
179:                         WHERE vfs_path = ? AND vfs_name= ?',
180:                        $this->_params['table']);
181:         try {
182:             $fileList = $this->_db->selectAll($sql, array($oldpath, $oldname));
183: 
184:         } catch (Horde_Db_Exception $e) {
185:             throw new Horde_Vfs_Exception($e);
186:         }
187:         if (!is_array($fileList) || count($fileList) < 1) {
188:             throw new Horde_Vfs_Exception('Unable to rename VFS file.');
189:         }
190: 
191:         if (strpos($newpath, '/') === false) {
192:             $parent = '';
193:             $path = $newpath;
194:         } else {
195:             list($parent, $path) = explode('/', $newpath, 2);
196:         }
197:         if (!$this->isFolder($parent, $path)) {
198:             $this->autocreatePath($newpath);
199:         }
200: 
201:         /* There may be one or more files with the same name but the user may
202:          * not have read access to them, so doesn't see them. So we have to
203:          * rename the one they have access to. */
204:         foreach ($fileList as $file) {
205:             if ($file['vfs_owner'] == $this->_params['user'] ||
206:                 $file['vfs_perms'] & self::FLAG_WRITE) {
207:                 $sql = sprintf('UPDATE %s SET vfs_path = ?, vfs_name = ?, vfs_modified = ?
208:                                 WHERE vfs_id = ?',
209:                                $this->_params['table']);
210:                 try {
211:                     $this->_db->update(
212:                         $sql,
213:                         array($newpath, $newname, time(), $file['vfs_id']));
214:                 } catch (Horde_Db_Exception $e) {
215:                     throw new Horde_Vfs_Exception($e);
216:                 }
217:             }
218:         }
219: 
220:         throw new Horde_Vfs_Exception(sprintf('Unable to rename VFS file %s/%s.', $oldpath, $oldname));
221:     }
222: 
223:     /**
224:      * Creates a folder on the VFS.
225:      *
226:      * @param string $path  Holds the path of directory to create folder.
227:      * @param string $name  Holds the name of the new folder.
228:      *
229:      * @throws Horde_Vfs_Exception
230:      */
231:     public function createFolder($path, $name)
232:     {
233:         /* Make sure we have write access to this and all parent paths. */
234:         if (strlen($path)) {
235:             $paths = explode('/', $path);
236:             $previous = '';
237: 
238:             foreach ($paths as $thispath) {
239:                 $sql = sprintf('SELECT vfs_owner, vfs_perms FROM %s
240:                                 WHERE vfs_path = ? AND vfs_name= ?',
241:                                $this->_params['table']);
242:                 try {
243:                     $results = $this->_db->selectAll($sql, array($previous, $thispath));
244:                 } catch (Horde_Db_Exception $e) {
245:                     throw new Horde_Vfs_Exception($e);
246:                 }
247:                 if (!is_array($results) || count($results) < 1) {
248:                     throw new Horde_Vfs_Exception('Unable to create VFS directory.');
249:                 }
250: 
251:                 $allowed = false;
252:                 foreach ($results as $result) {
253:                     if ($result['vfs_owner'] == $this->_params['user'] ||
254:                         $result['vfs_perms'] & self::FLAG_WRITE) {
255:                         $allowed = true;
256:                         break;
257:                     }
258:                 }
259: 
260:                 if (!$allowed) {
261:                     throw new Horde_Vfs_Exception('Access denied creating VFS directory.');
262:                 }
263: 
264:                 $previous = $thispath;
265:             }
266:         }
267: 
268:         $sql = sprintf('INSERT INTO %s
269:                         (vfs_type, vfs_path, vfs_name, vfs_modified, vfs_owner, vfs_perms)
270:                         VALUES (?, ?, ?, ?, ?, ?)',
271:                        $this->_params['table']);
272:         try {
273:             $this->_db->insert(
274:                 $sql,
275:                 array(self::FOLDER, $path, $name, time(), $this->_params['user'], 0));
276:         } catch (Horde_Db_Exception $e) {
277:             throw new Horde_Vfs_Exception($e);
278:         }
279:     }
280: 
281:     /**
282:      * Deletes a folder from the VFS.
283:      *
284:      * @param string $path        The path to delete the folder from.
285:      * @param string $name        The foldername to use.
286:      * @param boolean $recursive  Force a recursive delete?
287:      *
288:      * @throws Horde_Vfs_Exception
289:      */
290:     public function deleteFolder($path, $name, $recursive = false)
291:     {
292:         if ($recursive) {
293:             $this->emptyFolder($path . '/' . $name);
294:         } else {
295:             $list = $this->listFolder($path . '/' . $name);
296:             if (count($list)) {
297:                 throw new Horde_Vfs_Exception(sprintf('Unable to delete %s, the directory is not empty', $path . '/' . $name));
298:             }
299:         }
300: 
301:         $sql = sprintf('SELECT vfs_id, vfs_owner, vfs_perms FROM %s
302:                         WHERE vfs_path = ? AND vfs_name= ? AND vfs_type = ?',
303:                        $this->_params['table']);
304:         try {
305:             $fileList = $this->_db->selectAll($sql, array($path, $name, self::FOLDER));
306:         } catch (Horde_Db_Exception $e) {
307:             throw new Horde_Vfs_Exception($e);
308:         }
309:         if (!is_array($fileList) || count($fileList) < 1) {
310:             throw new Horde_Vfs_Exception('Unable to delete VFS directory.');
311:         }
312: 
313:         /* There may be one or more folders with the same name but as the user
314:          * may not have read access to them, they don't see them. So we have
315:          * to delete the one they have access to */
316:         foreach ($fileList as $file) {
317:             if ($file['vfs_owner'] == $this->_params['user'] ||
318:                 $file['vfs_perms'] & self::FLAG_WRITE) {
319:                 $sql = sprintf('DELETE FROM %s WHERE vfs_id = ?',
320:                                $this->_params['table']);
321:                 try {
322:                     $result = $this->_db->delete($sql, array($file['vfs_id']));
323:                 } catch (Horde_Db_Exception $e) {
324:                     throw new Horde_Vfs_Exception($e);
325:                 }
326:                 if ($result == 0) {
327:                     throw new Horde_Vfs_Exception('Unable to delete VFS directory.');
328:                 }
329: 
330:                 return $result;
331:             }
332:         }
333: 
334:         // FIXME: 'Access Denied deleting folder %s/%s'
335:         throw new Horde_Vfs_Exception('Unable to delete VFS directory.');
336:     }
337: 
338:     /**
339:      * Returns a list of the contents of a folder.
340:      *
341:      * @param string $path       The path of the directory.
342:      * @param mixed $filter      String/hash to filter file/dirname on.
343:      * @param boolean $dotfiles  Show dotfiles?
344:      * @param boolean $dironly   Show only directories?
345:      *
346:      * @return array  File list.
347:      * @throws Horde_Vfs_Exception
348:      */
349:     protected function _listFolder($path, $filter = null, $dotfiles = true,
350:                                    $dironly = false)
351:     {
352:         $length_op = $this->_getFileSizeOp();
353:         $sql = sprintf('SELECT vfs_name, vfs_type, vfs_modified, vfs_owner, vfs_perms, %s(vfs_data) length FROM %s
354:                         WHERE vfs_path = ? AND (vfs_owner = ? OR vfs_perms \&\& ?)',
355:                        $length_op, $this->_params['table']);
356:         try {
357:             $fileList = $this->_db->selectAll(
358:                 $sql,
359:                 array($path, $this->_params['user'], self::FLAG_READ));
360:         } catch (Horde_Db_Exception $e) {
361:             throw new Horde_Vfs_Exception($e);
362:         }
363: 
364:         $files = array();
365:         foreach ($fileList as $line) {
366:             // Filter out dotfiles if they aren't wanted.
367:             if (!$dotfiles && substr($line['vfs_name'], 0, 1) == '.') {
368:                 continue;
369:             }
370: 
371:             $file['name'] = stripslashes($line['vfs_name']);
372: 
373:             if ($line['vfs_type'] == self::FILE) {
374:                 $name = explode('.', $line['vfs_name']);
375: 
376:                 if (count($name) == 1) {
377:                     $file['type'] = '**none';
378:                 } else {
379:                     $file['type'] = Horde_String::lower($name[count($name) - 1]);
380:                 }
381: 
382:                 $file['size'] = $line['length'];
383:             } elseif ($line['vfs_type'] == self::FOLDER) {
384:                 $file['type'] = '**dir';
385:                 $file['size'] = -1;
386:             }
387: 
388:             $file['date'] = $line['vfs_modified'];
389:             $file['owner'] = $line['vfs_owner'];
390: 
391:             $line['vfs_perms'] = intval($line['vfs_perms']);
392:             $file['perms']  = ($line['vfs_type'] == self::FOLDER) ? 'd' : '-';
393:             $file['perms'] .= 'rw-';
394:             $file['perms'] .= ($line['vfs_perms'] & self::FLAG_READ) ? 'r' : '-';
395:             $file['perms'] .= ($line['vfs_perms'] & self::FLAG_WRITE) ? 'w' : '-';
396:             $file['perms'] .= '-';
397:             $file['group'] = '';
398: 
399:             // Filtering.
400:             if ($this->_filterMatch($filter, $file['name'])) {
401:                 unset($file);
402:                 continue;
403:             }
404:             if ($dironly && $file['type'] !== '**dir') {
405:                 unset($file);
406:                 continue;
407:             }
408: 
409:             $files[$file['name']] = $file;
410:             unset($file);
411:         }
412: 
413:         return $files;
414:     }
415: 
416:     /**
417:      * Changes permissions for an Item on the VFS.
418:      *
419:      * @param string $path        The path of directory of the item.
420:      * @param string $name        The name of the item.
421:      * @param string $permission  The permission to set in octal notation.
422:      *
423:      * @throws Horde_Vfs_Exception
424:      */
425:     public function changePermissions($path, $name, $permission)
426:     {
427:         $val = intval(substr($permission, -1));
428:         $perm = 0;
429:         $perm |= ($val & 4) ? self::FLAG_READ : 0;
430:         $perm |= ($val & 2) ? self::FLAG_WRITE : 0;
431: 
432:         $sql = sprintf('SELECT vfs_id, vfs_owner, vfs_perms FROM %s
433:                         WHERE vfs_path = ? AND vfs_name= ?',
434:                        $this->_params['table']);
435:         try {
436:             $fileList = $this->_db->selectAll($sql, array($path, $name));
437:         } catch (Horde_Db_Exception $e) {
438:             throw new Horde_Vfs_Exception($e);
439:         }
440:         if (!is_array($fileList) || count($fileList) < 1) {
441:             throw new Horde_Vfs_Exception('Unable to rename VFS file.');
442:         }
443: 
444:         /* There may be one or more files with the same name but the user may
445:          * not have read access to them, so doesn't see them. So we have to
446:          * chmod the one they have access to. */
447:         foreach ($fileList as $file) {
448:             if ($file['vfs_owner'] == $this->_params['user'] ||
449:                 $file['vfs_perms'] & self::FLAG_WRITE) {
450:                 $sql = sprintf('UPDATE %s SET vfs_perms = ?
451:                                 WHERE vfs_id = ?',
452:                                $this->_params['table']);
453:                 try {
454:                     $result = $this->_db->update($sql, array($perm, $file['vfs_id']));
455:                 } catch (Horde_Db_Exception $e) {
456:                     throw new Horde_Vfs_Exception($e);
457:                 }
458:             }
459:         }
460: 
461:         throw new Horde_Vfs_Exception(sprintf('Unable to change permission for VFS file %s/%s.', $path, $name));
462:     }
463: 
464: }
465: 
API documentation generated by ApiGen