config = $config; $this->filesTable = $config['tblNamePrefix'].'files'; $this->mdataTable = $config['tblNamePrefix'].'mdata'; $this->accessTable= $config['tblNamePrefix'].'access'; $this->storageDir = $config['storageDir']; $this->bufferDir = $config['bufferDir']; $this->transDir = $config['transDir']; $this->accessDir = $config['accessDir']; $this->dbc->setErrorHandling(PEAR_ERROR_RETURN); $this->rootId = $this->getRootNode(); $this->storId = $this->wd = $this->getObjId('StorageRoot', $this->rootId); $this->dbc->setErrorHandling(); } /** * Create new folder * * @param parid int, parent id * @param folderName string, name for new folder * @return id of new folder * @exception PEAR::error */ function bsCreateFolder($parid, $folderName) { return $this->addObj($folderName , 'Folder', $parid); } /** * Store new file in the storage * * @param parid int, parent id * @param fileName string, name for new file * @param mediaFileLP string, local path of media file * @param mdataFileLP string, local path of metadata file * @param gunid string, global unique id OPTIONAL * @param ftype string, internal file type * @param mdataLoc string 'file'|'string' (optional) * @return int * @exception PEAR::error */ function bsPutFile($parid, $fileName, $mediaFileLP, $mdataFileLP, $gunid=NULL, $ftype='unKnown', $mdataLoc='file') { $name = "$fileName"; $id = $this->addObj($name , $ftype, $parid); $ac =& StoredFile::insert( $this, $id, $name, $mediaFileLP, $mdataFileLP, $mdataLoc, $gunid, $ftype ); if($this->dbc->isError($ac)){ $res = $this->removeObj($id); return $ac; } if($ftype == 'playlist') $ac->setMime('application/smil'); return $id; } /** * Rename file * * @param id int, virt.file's local id * @param newName string * @return boolean or PEAR::error */ function bsRenameFile($id, $newName) { $parid = $this->getParent($id); switch($this->getObjType($id)){ case"audioclip": case"playlist": case"webstream": $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)){ // catch nonerror exception: //if($ac->getCode() != GBERR_FOBJNEX) return $ac; } $res = $ac->rename($newName); if($this->dbc->isError($res)) return $res; break; case"File": default: } return $this->renameObj($id, $newName); } /** * Move file * * @param id int, virt.file's local id * @param did int, destination folder local id * @return boolean or PEAR::error */ function bsMoveFile($id, $did) { $parid = $this->getParent($id); if($this->getObjType($did) !== 'Folder') return $this->dbc->raiseError( "BasicStor::moveFile: destination is not folder ($did)", GBERR_WRTYPE ); switch($this->getObjType($id)){ case"audioclip": case"playlist": case"webstream": case"File": case"Folder": return $this->moveObj($id, $did); break; default: return $this->dbc->raiseError( "BasicStor::moveFile: unsupported object to move, sorry.", GBERR_WRTYPE ); } } /** * Copy file * * @param id int, virt.file's local id * @param did int, destination folder local id * @return boolean or PEAR::error */ function bsCopyFile($id, $did) { $parid = $this->getParent($id); if($this->getObjType($did) !== 'Folder'){ return $this->dbc->raiseError( 'BasicStor::bsCopyFile: destination is not folder', GBERR_WRTYPE ); } switch($this->getObjType($id)){ case"audioclip": case"playlist": case"webstream": case"File": case"Folder": return $this->copyObj($id, $did); break; default: return $this->dbc->raiseError( "BasicStor::moveFile: unsupported object to copy, sorry.", GBERR_WRTYPE ); } } /** * Delete file * * @param id int, virt.file's local id * @param forced boolean, unconditional delete * @return true or PEAR::error */ function bsDeleteFile($id, $forced=FALSE) { $res = $this->removeObj($id, $forced); return $res; } /* ----------------------------------------------------- put, access etc. */ /** * Check validity of asscess/put token * * @param token string, access/put token * @param type string 'put'|'access'|'download' * @return boolean */ function bsCheckToken($token, $type='put') { $cnt = $this->dbc->getOne(" SELECT count(token) FROM {$this->accessTable} WHERE token=x'{$token}'::bigint AND type='$type' "); if($this->dbc->isError($cnt)){ return FALSE; } return ($cnt == 1); } /** * Get gunid from token * * @param token string, access/put token * @param type string 'put'|'access'|'download' * @return string */ function _gunidFromToken($token, $type='put') { $acc = $this->dbc->getRow(" SELECT to_hex(gunid)as gunid, ext FROM {$this->accessTable} WHERE token=x'{$token}'::bigint AND type='$type' "); if($this->dbc->isError($acc)){ return $acc; } $gunid = StoredFile::_normalizeGunid($acc['gunid']); if($this->dbc->isError($gunid)){ return $gunid; } return $gunid; } /** * Create and return access link to real file * * @param realFname string, local filepath to accessed file * (NULL for only increase access counter, no symlink) * @param ext string, useful filename extension for accessed file * @param gunid int, global unique id * @param type string 'access'|'download' * @return array with: seekable filehandle, access token */ function bsAccess($realFname, $ext, $gunid, $type='access') { $token = StoredFile::_createGunid(); if(!is_null($realFname)){ $linkFname = "{$this->accessDir}/$token.$ext"; if(!file_exists($realFname)){ return $this->dbc->raiseError( "BasicStor::bsAccess: real file not found ($realFname)", GBERR_FILEIO); } if(! @symlink($realFname, $linkFname)){ return $this->dbc->raiseError( "BasicStor::bsAccess: symlink create failed ($linkFname)", GBERR_FILEIO); } } $this->dbc->query("BEGIN"); $res = $this->dbc->query(" INSERT INTO {$this->accessTable} (gunid, token, ext, type, ts) VALUES (x'{$gunid}'::bigint, x'$token'::bigint, '$ext', '$type', now()) "); if($this->dbc->isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query(" UPDATE {$this->filesTable} SET currentlyAccessing=currentlyAccessing+1 WHERE gunid=x'{$gunid}'::bigint "); if($this->dbc->isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query("COMMIT"); if($this->dbc->isError($res)){ return $res; } return array('fname'=>$linkFname, 'token'=>$token); } /** * Release access link to real file * * @param token string, access token * @param type string 'access'|'download' * @return string, global unique ID */ function bsRelease($token, $type='access') { if(!$this->bsCheckToken($token, $type)){ return $this->dbc->raiseError( "BasicStor::bsRelease: invalid token ($token)" ); } $acc = $this->dbc->getRow(" SELECT to_hex(gunid)as gunid, ext FROM {$this->accessTable} WHERE token=x'{$token}'::bigint AND type='$type' "); if($this->dbc->isError($acc)){ return $acc; } $ext = $acc['ext']; $gunid = StoredFile::_normalizeGunid($acc['gunid']); $linkFname = "{$this->accessDir}/$token.$ext"; if(file_exists($linkFname)) if(! @unlink($linkFname)){ return $this->dbc->raiseError( "BasicStor::bsRelease: unlink failed ($linkFname)", GBERR_FILEIO); } $this->dbc->query("BEGIN"); $res = $this->dbc->query(" UPDATE {$this->filesTable} SET currentlyAccessing=currentlyAccessing-1 WHERE gunid=x'{$gunid}'::bigint AND currentlyAccessing>0 "); if($this->dbc->isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query(" DELETE FROM {$this->accessTable} WHERE token=x'$token'::bigint "); if($this->dbc->isError($res)){ $this->dbc->query("ROLLBACK"); return $res; } $res = $this->dbc->query("COMMIT"); if($this->dbc->isError($res)){ return $res; } return $gunid; } /** * Create and return downloadable URL for file * * @param id int, virt.file's local id * @param part string, 'media'|'metadata' * @return array with strings: * downloadable URL, download token, chsum, size, filename */ function bsOpenDownload($id, $part='media') { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; $gunid = $ac->gunid; switch($part){ case"media": $realfile = $ac->_getRealRADFname(); $ext = $ac->_getExt(); $filename = $ac->_getFileName(); break; case"metadata": $realfile = $ac->_getRealMDFname(); $ext = "xml"; $filename = $ac->_getFileName(); break; default: return $this->dbc->raiseError( "BasicStor::bsOpenDownload: unknown part ($part)" ); } $acc = $this->bsAccess($realfile, $ext, $gunid, 'download'); if($this->dbc->isError($acc)){ return $acc; } $url = $this->getUrlPart()."access/".basename($acc['fname']); $chsum = md5_file($realfile); $size = filesize($realfile); return array( 'url'=>$url, 'token'=>$acc['token'], 'chsum'=>$chsum, 'size'=>$size, 'filename'=>$filename ); } /** * Discard downloadable URL * * @param token string, download token * @param part string, 'media'|'metadata' * @return string, gunid */ function bsCloseDownload($token, $part='media') { if(!$this->bsCheckToken($token, 'download')){ return $this->dbc->raiseError( "BasicStor::bsCloseDownload: invalid token ($token)" ); } return $this->bsRelease($token, 'download'); } /** * Create writable URL for HTTP PUT method file insert * * @param chsum string, md5sum of the file having been put * @param gunid string, global unique id * @return array with: writable URL, PUT token */ function bsOpenPut($chsum, $gunid) { $ext = ''; $token = StoredFile::_createGunid(); $res = $this->dbc->query(" DELETE FROM {$this->accessTable} WHERE token=x'$token'::bigint "); if($this->dbc->isError($res)){ return $res; } $res = $this->dbc->query(" INSERT INTO {$this->accessTable} (gunid, token, ext, chsum, type, ts) VALUES (x'{$gunid}'::bigint, x'$token'::bigint, '$ext', '$chsum', 'put', now()) "); if($this->dbc->isError($res)){ return $res; } $fname = "{$this->accessDir}/$token"; touch($fname); // is it needed? $url = $this->getUrlPart()."xmlrpc/put.php?token=$token"; return array('url'=>$url, 'token'=>$token); } /** * Get file from writable URL and return local filename. * Caller should move or unlink this file. * * @param token string, PUT token * @return string, local path of the file having been put */ function bsClosePut($token) { $token = StoredFile::_normalizeGunid($token); if(!$this->bsCheckToken($token, 'put')){ return $this->dbc->raiseError( "BasicStor::bsClosePut: invalid token ($token)", GBERR_TOKEN ); } $chsum = $this->dbc->getOne(" SELECT chsum FROM {$this->accessTable} WHERE token=x'{$token}'::bigint "); $res = $this->dbc->query(" DELETE FROM {$this->accessTable} WHERE token=x'$token'::bigint "); if($this->dbc->isError($res)){ return $res; } $fname = "{$this->accessDir}/$token"; $md5sum = md5_file($fname); if($chsum != $md5sum){ if(file_exists($fname)) @unlink($fname); return $this->dbc->raiseError( "BasicStor::bsClosePut: md5sum does not match (token=$token)", GBERR_PUT ); } return $fname; } /** * Check uploaded file * * @param token string, put token * @return hash, ( * status: boolean, * size: int - filesize * expectedsum: string - expected checksum * realsum: string - checksum of uploaded file * ) */ function bsCheckPut($token) { if(!$this->bsCheckToken($token, 'put')){ return $this->dbc->raiseError( "BasicStor::bsClosePut: invalid token ($token)" ); } $chsum = $this->dbc->getOne(" SELECT chsum FROM {$this->accessTable} WHERE token=x'{$token}'::bigint "); if($this->dbc->isError($chsum)){ return $chsum; } $fname = "{$this->accessDir}/$token"; $md5sum = md5_file($fname); $size = filesize($fname); $status = ($chsum == $md5sum); return array( 'status'=>$status, 'size'=>$size, 'expectedsum'=>$chsum, 'realsum'=>$md5sum, ); } /** * Return starting part of storageServer URL * * @return string, url */ function getUrlPart() { $host = $this->config['storageUrlHost']; $port = $this->config['storageUrlPort']; $path = $this->config['storageUrlPath']; return "http://$host:$port$path/"; } /* ---------------------------------------------- replicas, versions etc. */ /** * Create replica.
* TODO: NOT FINISHED * * @param id int, virt.file's local id * @param did int, destination folder local id * @param replicaName string, name of new replica * @return int, local id of new object */ function bsCreateReplica($id, $did, $replicaName) { return $this->dbc->raiseError( 'BasicStor::bsCreateReplica: not implemented', GBERR_NOTIMPL ); // --- if($this->getObjType($did) !== 'Folder') return $this->dbc->raiseError( 'BasicStor::bsCreateReplica: dest is not folder', GBERR_WRTYPE ); if($replicaName=='') $replicaName = $this->getObjName($id); while(($exid = $this->getObjId($replicaName, $did))<>'') { $replicaName.='_R'; } $rid = $this->addObj($replicaName , 'Replica', $did, 0, $id); if($this->dbc->isError($rid)) return $rid; # $this->addMdata($this->_pathFromId($rid), 'isReplOf', $id); return $rid; } /** * Create version.
* TODO: NOT FINISHED * * @param id int, virt.file's local id * @param did int, destination folder local id * @param versionLabel string, name of new version * @return int, local id of new object */ function bsCreateVersion($id, $did, $versionLabel) { return $this->dbc->raiseError( 'BasicStor::bsCreateVersion: not implemented', GBERR_NOTIMPL ); } /* ------------------------------------------------------------- metadata */ /** * Replace metadata with new XML file or string * * @param id int, virt.file's local id * @param mdata string, local path of metadata XML file * @param mdataLoc string 'file'|'string' * @return boolean or PEAR::error */ function bsReplaceMetadata($id, $mdata, $mdataLoc='file') { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; return $ac->replaceMetaData($mdata, $mdataLoc); } /** * Get metadata as XML string * * @param id int, virt.file's local id * @return string or PEAR::error */ function bsGetMetadata($id) { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; return $ac->getMetaData(); } /** * Get metadata element value * * @param id int, virt.file's local id * @param category string, metadata element name * @param lang string, optional xml:lang value for select language version * @return array of matching records (as hash {id, value, attrs}) * @see Metadata::getMetadataValue */ function bsGetMetadataValue($id, $category, $lang=NULL) { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; return $ac->md->getMetadataValue($category, $lang); } /** * Set metadata element value * * @param id int, virt.file's local id * @param category string, metadata element identification (e.g. dc:title) * @param value string/NULL value to store, if NULL then delete record * @param lang string, optional xml:lang value for select language version * @param mid int, metadata record id (OPTIONAL on unique elements) * @param container string, container element name for insert * @return boolean */ function bsSetMetadataValue( $id, $category, $value, $lang=NULL, $mid=NULL, $container='metadata') { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; $res = $ac->md->setMetadataValue( $category, $value, $lang, $mid, $container); if($this->dbc->isError($res)) return $res; $r = $ac->md->regenerateXmlFile(); if($this->dbc->isError($r)) return $r; return $res; } /** * Search in local metadata database. * * @param criteria hash, with following structure:
* * @param limit int, limit for result arrays (0 means unlimited) * @param offset int, starting point (0 means without offset) * @return hash, fields: * results : array with gunid strings * cnt : integer - number of matching gunids * of files have been found * @see DataEngine */ function bsLocalSearch($criteria, $limit=0, $offset=0) { require_once "DataEngine.php"; $de =& new DataEngine($this); return $de->localSearch($criteria, $limit, $offset); } /** * Return values of specified metadata category * * @param category string, metadata category name * with or without namespace prefix (dc:title, author) * @param limit int, limit for result arrays (0 means unlimited) * @param offset int, starting point (0 means without offset) * @param criteria hash, see bsLocalSearch method * @return hash, fields: * results : array with gunid strings * cnt : integer - number of matching values * @see DataEngine */ function bsBrowseCategory($category, $limit=0, $offset=0, $criteria=NULL) { require_once "DataEngine.php"; $de =& new DataEngine($this); return $de->browseCategory($category, $limit, $offset, $criteria); } /* --------------------------------------------------------- info methods */ /** * List files in folder * * @param id int, local id of folder * @return array */ function bsListFolder($id) { if($this->getObjType($id) !== 'Folder') return $this->dbc->raiseError( 'BasicStor::bsListFolder: not a folder', GBERR_NOTF ); $listArr = $this->getDir($id, 'id, name, type, param as target', 'name'); if($this->dbc->isError($listArr)) return $listArr; foreach($listArr as $i=>$v){ if($v['type'] == 'Folder') break; $gunid = $this->_gunidFromId($v['id']); if($this->dbc->isError($gunid)) return $gunid; if(is_null($gunid)){ unset($listArr[$i]); break; } $listArr[$i]['type'] = $r = $this->_getType($gunid); if($this->dbc->isError($r)) return $r; $listArr[$i]['gunid'] = $gunid; if(StoredFIle::_getState($gunid) == 'incomplete') unset($listArr[$i]); } return $listArr; } /** * Analyze media file for internal metadata information * * @param id int, virt.file's local id * @return array */ function bsAnalyzeFile($id) { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; $ia = $ac->analyzeMediaFile(); return $ia; } /** * List files in folder * * @param id int, local id of object * @param relPath string, relative path * @return array */ function getObjIdFromRelPath($id, $relPath='.') { $relPath = trim(urldecode($relPath)); //if($this->getObjType($id) !== 'Folder') $nid = $this->getParent($id); if($this->dbc->isError($nid)) return $nid; if(is_null($nid)){ return $this->dbc->raiseError("null parent for id=$id"); } //else $nid = $id; if(substr($relPath, 0, 1)=='/'){ $nid=$this->storId; } $a = split('/', $relPath); foreach($a as $i=>$pathItem){ switch($pathItem){ case".": break; case"..": if($nid != $this->storId){ $nid = $this->getParent($nid); if($this->dbc->isError($nid)) return $nid; if(is_null($nid)){ return $this->dbc->raiseError( "null parent for $nid"); } } break; case"": break; default: $nid = $this->getObjId($pathItem, $nid); if($this->dbc->isError($nid)) return $nid; if(is_null($nid)){ return $this->dbc->raiseError( "Object $pathItem not found (from id=$id)"); } } } return $nid; } /** * Check if file exists in the storage * * @param id int, local id * @param ftype string, internal file type * @return boolean */ function bsExistsFile($id, $ftype=NULL) { $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)){ // catch some exceptions switch($ac->getCode()){ case GBERR_FILENEX: case GBERR_FOBJNEX: return FALSE; break; default: return $ac; } } $realFtype = $this->_getType($ac->gunid); if(!is_null($ftype) && ( ($realFtype != $ftype) // webstreams are subset of audioclips && !($realFtype == 'webstream' && $ftype == 'audioclip') )) return FALSE; return TRUE; } /* ---------------------------------------------------- redefined methods */ /** * Get object type by id. * (RootNode, Folder, File, ) * * @param oid int, local object id * @return string/err */ function getObjType($oid) { $type = parent::getObjType($oid); if($type == 'File'){ $gunid = $this->_gunidFromId($oid); if($this->dbc->isError($gunid)) return $gunid; $ftype = $this->_getType($gunid); if($this->dbc->isError($ftype)) return $ftype; if(!is_null($ftype)) $type=$ftype; } return $type; } /** * Add new user with home folder * * @param login string * @param pass string OPT * @return int/err */ function addSubj($login, $pass=NULL) { $uid = parent::addSubj($login, $pass); if($this->dbc->isError($uid)) return $uid; if($this->isGroup($uid) === FALSE){ $fid = $this->bsCreateFolder($this->storId, $login); if($this->dbc->isError($fid)) return $fid; $res = $this->addPerm($uid, '_all', $fid, 'A'); if($this->dbc->isError($res)) return $res; if(!$this->config['isArchive']){ $res =$this->addSubj2Gr($login, $this->config['StationPrefsGr']); if($this->dbc->isError($res)) return $res; $res =$this->addSubj2Gr($login, $this->config['AllGr']); if($this->dbc->isError($res)) return $res; $pfid = $this->bsCreateFolder($fid, 'public'); if($this->dbc->isError($pfid)) return $pfid; $res = $this->addPerm($uid, '_all', $pfid, 'A'); if($this->dbc->isError($res)) return $res; $allGrId = $this->getSubjId($this->config['AllGr']); if($this->dbc->isError($allGrId)) return $allGrId; $res = $this->addPerm($allGrId, 'read', $pfid, 'A'); if($this->dbc->isError($res)) return $res; } } return $uid; } /** * Remove user by login or by uid and his home folder * * @param login string * @param uid int OPT * @return boolean/err */ function removeSubj($login, $uid=NULL) { $res = parent::removeSubj($login); if($this->dbc->isError($res)) return $res; $id = $this->getObjId($login, $this->storId); if($this->dbc->isError($id)) return $id; if(!is_null($id)){ // remove home folder: $res = $this->bsDeleteFile($id); if($this->dbc->isError($res)) return $res; } return TRUE; } /* ================================================== "protected" methods */ /** * Check authorization - auxiliary method * * @param acts array of actions * @param pars array of parameters - e.g. ids * @param sessid string, session id * @return true or PEAR::error */ function _authorize($acts, $pars, $sessid='') { $userid = $this->getSessUserId($sessid); if(!is_array($pars)) $pars = array($pars); if(!is_array($acts)) $acts = array($acts); $perm = true; foreach($acts as $i=>$action){ $res = $this->checkPerm($userid, $action, $pars[$i]); if($this->dbc->isError($res)) return $res; $perm = $perm && $res; } if($perm) return TRUE; $adesc = "[".join(',',$acts)."]"; return $this->dbc->raiseError( "BasicStor::$adesc: access denied", GBERR_DENY); } /** * Return users's home folder local ID * * @param sessid string, session ID * @return local folder id */ function _getHomeDirId($sessid) { $login = $this->getSessLogin($sessid); if($this->dbc->isError($login)) return $login; $parid = $this->getObjId($login, $this->storId); if($this->dbc->isError($parid)) return $parid; if(is_null($parid)){ return $this->dbc->raiseError("BasicStor::_getHomeDirId: ". "homedir not found", GBERR_NOTF); } return $parid; } /** * Get local id from global id * * @param gunid string global id * @return int local id */ function _idFromGunid($gunid) { return $this->dbc->getOne( "SELECT id FROM {$this->filesTable} WHERE gunid=x'$gunid'::bigint" ); } /** * Get global id from local id * * @param id int local id * @return string global id */ function _gunidFromId($id) { if(!is_numeric($id)) return NULL; $gunid = $this->dbc->getOne(" SELECT to_hex(gunid)as gunid FROM {$this->filesTable} WHERE id='$id' "); if($this->dbc->isError($gunid)) return $gunid; if(is_null($gunid)) return NULL; return StoredFile::_normalizeGunid($gunid); } /** * Get storage-internal file type * * @param gunid string, global unique id of file * @return string, see install() */ function _getType($gunid) { $ftype = $this->dbc->getOne(" SELECT ftype FROM {$this->filesTable} WHERE gunid=x'$gunid'::bigint "); return $ftype; } /** * Check gunid format * * @param gunid string, global unique ID * @return boolean */ function _checkGunid($gunid) { $res = preg_match("|^([0-9a-fA-F]{16})?$|", $gunid); return $res; } /** * Returns if gunid is free * */ function _gunidIsFree($gunid) { $cnt = $this->dbc->getOne(" SELECT count(*) FROM {$this->filesTable} WHERE gunid=x'{$this->gunid}'::bigint "); if($this->dbc->isError($cnt)) return $cnt; if($cnt > 0) return FALSE; return TRUE; } /** * Set playlist edit flag * * @param playlistId string, playlist global unique ID * @param val boolean, set/clear of edit flag * @return boolean, previous state */ function _setEditFlag($playlistId, $val=TRUE) { $ac =& StoredFile::recallByGunid($this, $playlistId); if($this->dbc->isError($ac)) return $ac; $state = $ac->_getState(); if($val){ $ac->setState('edited'); } else{ $ac->setState('ready'); } return ($state == 'edited'); } /** * Check if playlist is marked as edited * * @param playlistId string, playlist global unique ID * @return boolean */ function _isEdited($playlistId) { $ac =& StoredFile::recallByGunid($this, $playlistId); if($this->dbc->isError($ac)) return $ac; return $ac->isEdited($playlistId); } /* ---------------------------------------- redefined "protected" methods */ /** * Copy virtual file.
* Redefined from parent class. * * @return int, new object local id */ function copyObj($id, $newParid, $after=NULL) { $parid = $this->getParent($id); $nid = parent::copyObj($id, $newParid, $after); if($this->dbc->isError($nid)) return $nid; switch($this->getObjType($id)){ case"audioclip": case"playlist": case"webstream": $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)){ return $ac; } $ac2 =& StoredFile::copyOf($ac, $nid); $ac2->rename($this->getObjName($nid)); break; case"File": default: } return $nid; } /** * Move virtual file.
* Redefined from parent class. * * @return boolean */ function moveObj($id, $newParid, $after=NULL) { $parid = $this->getParent($id); switch($this->getObjType($id)){ case"audioclip": case"playlist": case"webstream": $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)){ return $ac; } if($ac->isEdited()) return $this->dbc->raiseError( 'BasicStor::moveObj: is edited'); if($ac->isAccessed()) return $this->dbc->raiseError( 'BasicStor::moveObj: is accessed'); break; default: } $nid = parent::moveObj($id, $newParid, $after); if($this->dbc->isError($nid)) return $nid; return TRUE; } /** * Optionaly remove virtual file with the same name and add new one.
* Redefined from parent class. * * @return id */ function addObj($name, $type, $parid=1, $aftid=NULL, $param='') { $exid = $this->getObjId($name, $parid); if($this->dbc->isError($exid)) return $exid; if(!is_null($exid)){ $this->removeObj($exid); } return parent::addObj($name, $type, $parid, $aftid, $param); } /** * Remove virtual file.
* Redefined from parent class. * * @param id int, local id of removed object * @param forced boolean, unconditional delete * @return true or PEAR::error */ function removeObj($id, $forced=FALSE) { switch($ot = $this->getObjType($id)){ case"audioclip": case"playlist": case"webstream": $ac =& StoredFile::recall($this, $id); if($this->dbc->isError($ac)) return $ac; if($ac->isEdited() && !$forced){ return $this->dbc->raiseError( 'BasicStor.php: removeObj: is edited' ); } if($ac->isAccessed() && !$forced){ return $this->dbc->raiseError( 'BasicStor.php: removeObj: is accessed' ); } $ac->delete(); break; case"File": case"Folder": case"Replica": break; default: return $this->dbc->raiseError( "BasicStor::bsDeleteFile: unknown obj type ($ot)" ); } $res = parent::removeObj($id); if($this->dbc->isError($res)) return $res; return TRUE; } /* =============================================== test and debug methods */ /** * Reset storageServer for debugging. * * @param input string */ function resetStorage($input='') { $this->deleteData(); if(!$this->config['isArchive']){ $tr =& new Transport($this->dbc, $this, $this->config); $tr->resetData(); } $rootHD = $this->getObjId('root', $this->storId); include"../tests/sampleData.php"; $res = array( 'audioclips'=>array(), 'playlists'=>array(), 'webstreams'=>array() ); foreach($sampleData as $k=>$it){ $type = $it['type']; $xml = $it['xml']; if(isset($it['gunid'])) $gunid = $it['gunid']; else $gunid = ''; switch($type){ case"audioclip": $media = $it['media']; $fname = basename($media); break; case"playlist": case"webstream": $media = ''; $fname = basename($xml); break; } $r = $this->bsPutFile( $rootHD, $fname, $media, $xml, $gunid, $type ); if(PEAR::isError($r)){ return $r; } $res["{$type}s"][] = $this->_gunidFromId($r); } return $res; } /** * dump * */ function dump($id='', $indch=' ', $ind='', $format='{name}') { if($id=='') $id = $this->storId; return parent::dump($id, $indch, $ind, $format); } /** * * */ function dumpDir($id='', $format='$o["name"]') { if($id=='') $id = $this->storId; $arr = $this->getDir($id, 'id,name'); // if($this->doDebug){ $this->debug($arr); exit; } $arr = array_map(create_function('$o', 'return "'.$format.'";'), $arr); return join('', $arr); } /** * * */ function debug($va) { echo"
\n"; print_r($va); #exit;
    }

    /**
     *  deleteData
     *
     *  @return void
     */
    function deleteData()
    {
//        $this->dbc->query("DELETE FROM {$this->filesTable}");
        $ids = $this->dbc->getAll("SELECT id FROM {$this->filesTable}");
        if(is_array($ids)) foreach($ids as $i=>$item){
            $this->bsDeleteFile($item['id'], TRUE);
        }
        parent::deleteData();
        $this->initData();
    }
    /**
     *  testData
     *
     */
    function testData($d='')
    {
        $exdir = dirname(__FILE__).'/tests';
        $o[] = $this->addSubj('test1', 'a');
        $o[] = $this->addSubj('test2', 'a');
        $o[] = $this->addSubj('test3', 'a');
        $o[] = $this->addSubj('test4', 'a');

        $o[] = $t1hd = $this->getObjId('test1', $this->storId);
        $o[] = $t1d1 = $this->bsCreateFolder($t1hd, 'test1_folder1');
        $o[] = $this->bsCreateFolder($t1hd, 'test1_folder2');
        $o[] = $this->bsCreateFolder($t1d1, 'test1_folder1_1');
        $o[] = $t1d12 = $this->bsCreateFolder($t1d1, 'test1_folder1_2');

        $o[] = $t2hd = $this->getObjId('test2', $this->storId);
        $o[] = $this->bsCreateFolder($t2hd, 'test2_folder1');

        $o[] = $this->bsPutFile($t1hd, 'file1.mp3', "$exdir/ex1.mp3", '', NULL, 'audioclip');
        $o[] = $this->bsPutFile($t1d12, 'file2.wav', "$exdir/ex2.wav", '', NULL, 'audioclip');
/*
*/
        $this->tdata['storage'] = $o;
    }

    /**
     *  test
     *
     */
    function test()
    {
        $this->test_log = '';
//        if($this->dbc->isError($p = parent::test())) return $p;
        $this->deleteData();
        $this->testData();
        $this->test_correct = "    StorageRoot
        root
        test1
            file1.mp3
            public
            test1_folder1
                test1_folder1_1
                test1_folder1_2
                    file2.wav
            test1_folder2
        test2
            public
            test2_folder1
        test3
            public
        test4
            public
";
        $this->test_dump = $this->dumpTree($this->storId, '    ', '    ', '{name}');
        if($this->test_dump==$this->test_correct)
            { $this->test_log.="# BasicStor::test: OK\n"; return true; }
        else $this->dbc->raiseError('BasicStor::test:', 1, PEAR_ERROR_DIE, '%s'.
            "
\ncorrect:\n.{$this->test_correct}.\n".
            "dump:\n.{$this->test_dump}.\n
\n"); } /** * initData - initialize * */ function initData() { $this->rootId = $this->getRootNode(); $this->storId = $this->wd = $this->addObj('StorageRoot', 'Folder', $this->rootId); $rootUid = parent::addSubj('root', $this->config['tmpRootPass']); $res = $this->addPerm($rootUid, '_all', $this->rootId, 'A'); $fid = $this->bsCreateFolder($this->storId, 'root'); if(!$this->config['isArchive']){ $stPrefGr = parent::addSubj($this->config['StationPrefsGr']); $this->addSubj2Gr('root', $this->config['StationPrefsGr']); $stPrefGr = parent::addSubj($this->config['AllGr']); $this->addSubj2Gr('root', $this->config['AllGr']); } } /** * install - create tables * * file states: * * file types: * */ function install() { parent::install(); #echo "{$this->filesTable}\n"; $r = $this->dbc->query("CREATE TABLE {$this->filesTable} ( id int not null, gunid bigint not null, -- global unique ID name varchar(255) not null default'', -- human file id ;) mime varchar(255) not null default'', -- mime type ftype varchar(128) not null default'', -- file type state varchar(128) not null default'empty', -- file state currentlyAccessing int not null default 0 -- access counter )"); if($this->dbc->isError($r)) return $r; $this->dbc->query("CREATE UNIQUE INDEX {$this->filesTable}_id_idx ON {$this->filesTable} (id)"); $this->dbc->query("CREATE UNIQUE INDEX {$this->filesTable}_gunid_idx ON {$this->filesTable} (gunid)"); $this->dbc->query("CREATE INDEX {$this->filesTable}_name_idx ON {$this->filesTable} (name)"); #echo "{$this->mdataTable}\n"; $this->dbc->createSequence("{$this->mdataTable}_id_seq"); $r = $this->dbc->query("CREATE TABLE {$this->mdataTable} ( id int not null, gunid bigint, subjns varchar(255), -- subject namespace shortcut/uri subject varchar(255) not null default '', predns varchar(255), -- predicate namespace shortcut/uri predicate varchar(255) not null, predxml char(1) not null default 'T', -- Tag or Attribute objns varchar(255), -- object namespace shortcut/uri object text )"); if($this->dbc->isError($r)) return $r; $this->dbc->query("CREATE UNIQUE INDEX {$this->mdataTable}_id_idx ON {$this->mdataTable} (id)"); $this->dbc->query("CREATE INDEX {$this->mdataTable}_gunid_idx ON {$this->mdataTable} (gunid)"); $this->dbc->query("CREATE INDEX {$this->mdataTable}_subj_idx ON {$this->mdataTable} (subjns, subject)"); $this->dbc->query("CREATE INDEX {$this->mdataTable}_pred_idx ON {$this->mdataTable} (predns, predicate)"); #echo "{$this->accessTable}\n"; $r = $this->dbc->query("CREATE TABLE {$this->accessTable} ( gunid bigint, token bigint, chsum char(32) not null default'', ext varchar(128) not null default'', type varchar(20) not null default'', ts timestamp )"); if($this->dbc->isError($r)) return $r; $this->dbc->query("CREATE INDEX {$this->accessTable}_token_idx ON {$this->accessTable} (token)"); $this->dbc->query("CREATE INDEX {$this->accessTable}_gunid_idx ON {$this->accessTable} (gunid)"); if(!file_exists($this->bufferDir)){ mkdir($this->bufferDir, 02775); chmod($this->bufferDir, 02775); // may be obsolete } $this->initData(); } /** * id subjns subject predns predicate objns object * y1 literal xmbf NULL namespace literal http://www.sotf.org/xbmf * x1 gunid xbmf contributor NULL NULL * x2 mdid x1 xbmf role literal Editor * * predefined shortcuts: * _L = literal * _G = gunid (global id of media file) * _I = mdid (local id of metadata record) * _nssshortcut = namespace shortcut definition * _blank = blank node */ /** * uninstall * * @return void */ function uninstall() { $this->dbc->query("DROP TABLE {$this->mdataTable}"); $this->dbc->dropSequence("{$this->mdataTable}_id_seq"); $this->dbc->query("DROP TABLE {$this->filesTable}"); $this->dbc->query("DROP TABLE {$this->accessTable}"); $d = @dir($this->storageDir); while (is_object($d) && (false !== ($entry = $d->read()))){ if(filetype("{$this->storageDir}/$entry")=='dir' && $entry!='CVS' && strlen($entry)==3) { $dd = dir("{$this->storageDir}/$entry"); while (false !== ($ee = $dd->read())){ if(substr($ee, 0, 1)!=='.') unlink("{$this->storageDir}/$entry/$ee"); } $dd->close(); rmdir("{$this->storageDir}/$entry"); } } if(is_object($d)) $d->close(); if(file_exists($this->bufferDir)){ $d = dir($this->bufferDir); while (false !== ($entry = $d->read())) if(substr($entry,0,1)!='.') { unlink("{$this->bufferDir}/$entry"); } $d->close(); @rmdir($this->bufferDir); } parent::uninstall(); } /** * Aux logging for debug * * @param msg string - log message */ function debugLog($msg) { $fp=fopen("{$this->storageDir}/log", "a") or die("Can't write to log\n"); fputs($fp, date("H:i:s").">$msg<\n"); fclose($fp); } } ?>