diff --git a/core/controllers/api/BitstreamController.php b/core/controllers/api/BitstreamController.php index 3dd3fa0a2..14e0be7bf 100644 --- a/core/controllers/api/BitstreamController.php +++ b/core/controllers/api/BitstreamController.php @@ -29,8 +29,11 @@ class Rest_BitstreamController extends ApiController */ public function indexAction() { - $apiFunctions['default'] = 'bitstreamCount'; - $apiFunctions['count'] = 'bitstreamCount'; + $apiFunctions = array( + 'default' => 'bitstreamCount', + 'exists' => 'bitstreamCount', + 'download' => 'bitstreamDownloadByChecksum' + ); $this->_genericAction($this->_request->getParams(), 'index', $apiFunctions); } @@ -60,7 +63,10 @@ public function headAction() */ public function getAction() { - $apiFunctions['default'] = 'bitstreamGet'; + $apiFunctions = array( + 'default' => 'bitstreamGet', + 'download' => 'bitstreamDownloadById' + ); $this->_genericAction($this->_request->getParams(), 'get', $apiFunctions); } diff --git a/core/controllers/api/FolderController.php b/core/controllers/api/FolderController.php index ea6657ec7..002929b58 100644 --- a/core/controllers/api/FolderController.php +++ b/core/controllers/api/FolderController.php @@ -53,7 +53,8 @@ public function getAction() $apiFunctions = array( 'default' => 'folderGet', 'children' => 'folderChildren', - 'permission' => 'folderListPermissions' + 'permission' => 'folderListPermissions', + 'download' => 'folderDownload' ); $this->_genericAction($this->_request->getParams(), 'get', $apiFunctions); } diff --git a/core/controllers/api/ItemController.php b/core/controllers/api/ItemController.php index 4e74c826a..41577f3a2 100644 --- a/core/controllers/api/ItemController.php +++ b/core/controllers/api/ItemController.php @@ -57,7 +57,8 @@ public function getAction() $apiFunctions = array( 'default' => 'itemGet', 'metadata'=> 'itemGetmetadata', - 'permission' => 'itemListPermissions' + 'permission' => 'itemListPermissions', + 'download' => 'itemDownload' ); $this->_genericAction($this->_request->getParams(), 'get', $apiFunctions); } diff --git a/core/controllers/api/SystemController.php b/core/controllers/api/SystemController.php index d613841f4..bba73727f 100644 --- a/core/controllers/api/SystemController.php +++ b/core/controllers/api/SystemController.php @@ -39,7 +39,8 @@ public function indexAction() 'metadataqualifiers' => 'metadataQualifiersList', 'uploadeoffset' => 'uploadGetoffset', 'metadatatypes' => 'metadataTypesList', - 'metadaelements' => 'metadataElementsList' + 'metadaelements' => 'metadataElementsList', + 'uploadtoken' => 'uploadGeneratetoken' ); $this->_genericAction($this->_request->getParams(), 'index', $apiFunctions); } @@ -73,6 +74,8 @@ public function postAction() $apiFunctions = array( 'default' => 'adminDatabaseCleanup', 'databasecleanup' => 'adminDatabaseCleanup', + 'defaultapikey' => 'userApikeyDefault', + 'upload' => 'uploadPerform' ); $this->_genericAction($this->_request->getParams(), 'post', $apiFunctions); } diff --git a/core/controllers/components/ApiComponent.php b/core/controllers/components/ApiComponent.php index e195f509b..db10762a2 100644 --- a/core/controllers/components/ApiComponent.php +++ b/core/controllers/components/ApiComponent.php @@ -57,6 +57,15 @@ private function _validateParams($args, $requiredList) } } + private function _getApiSetup() + { + $apiSetup = array(); + $apiSetup['testing'] = Zend_Registry::get('configGlobal')->environment == 'testing'; + $utilityComponent = MidasLoader::loadComponent('Utility'); + $apiSetup['tmpDirectory'] = $utilityComponent->getTempDirectory(); + return $apiSetup; + } + /** * Helper function to return any extra fields that should be passed with an item * @param item The item dao @@ -500,6 +509,8 @@ function login($args) /** * Returns the user's default API key given their username and password. + * @path /system/defaultapikey + * @http POST * @param email The user's email * @param password The user's password * @return Array with a single key, 'apikey', whose value is the user's default api key @@ -564,6 +575,308 @@ function userApikeyDefault($args) } } + /** + * Generate a unique upload token. Either itemid or folderid is required, + but both are not allowed. + * @path /system/uploadtoken + * @http GET + * @param useSession (Optional) Authenticate using the current Midas session + * @param token (Optional) Authentication token + * @param itemid (Optional) + The id of the item to upload into. + * @param folderid (Optional) + The id of the folder to create a new item in and then upload to. + The new item will have the same name as filename unless itemname + is supplied. + * @param filename The filename of the file you will upload, will be used as the + bitstream's name and the item's name (unless itemname is supplied). + * @param itemprivacy (Optional) + When passing the folderid param, the privacy status of the newly + created item, Default 'Public', possible values [Public|Private]. + * @param itemdescription (Optional) + When passing the folderid param, the description of the item, + if not supplied the item's description will be blank. + * @param itemname (Optional) + When passing the folderid param, the name of the newly created item, + if not supplied, the item will have the same name as filename. + * @param checksum (Optional) The md5 checksum of the file to be uploaded. + * @return An upload token that can be used to upload a file. + If folderid is passed instead of itemid, a new item will be created + in that folder, but the id of the newly created item will not be + returned. If the id of the newly created item is needed, + then call the /item (POST) api instead. + If checksum is passed and the token returned is blank, the + server already has this file and there is no need to follow this + call with a call to /system/upload, as the passed in + file will have been added as a bitstream to the item's latest + revision, creating a new revision if one doesn't exist. + */ + function uploadGeneratetoken($args) + { + $this->_validateParams($args, array('filename')); + if(!array_key_exists('itemid', $args) && !array_key_exists('folderid', $args)) + { + throw new Exception('Parameter itemid or folderid must be defined', MIDAS_INVALID_PARAMETER); + } + if(array_key_exists('itemid', $args) && array_key_exists('folderid', $args)) + { + throw new Exception('Parameter itemid or folderid must be defined, but not both', MIDAS_INVALID_PARAMETER); + } + + $userDao = $this->_getUser($args); + if(!$userDao) + { + throw new Exception('Anonymous users may not upload', MIDAS_INVALID_POLICY); + } + + $itemModel = MidasLoader::loadModel('Item'); + if(array_key_exists('itemid', $args)) + { + $item = $itemModel->load($args['itemid']); + if(!$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_WRITE)) + { + throw new Exception('Invalid policy or itemid', MIDAS_INVALID_POLICY); + } + } + else if(array_key_exists('folderid', $args)) + { + $folderModel = MidasLoader::loadModel('Folder'); + $folder = $folderModel->load($args['folderid']); + if($folder == false) + { + throw new Exception('Parent folder corresponding to folderid doesn\'t exist', MIDAS_INVALID_PARAMETER); + } + if(!$folderModel->policyCheck($folder, $userDao, MIDAS_POLICY_WRITE)) + { + throw new Exception('Invalid policy or folderid', MIDAS_INVALID_POLICY); + } + // create a new item in this folder + $itemname = isset($args['itemname']) ? $args['itemname'] : $args['filename']; + $description = isset($args['itemdescription']) ? $args['itemdescription'] : ''; + $item = $itemModel->createItem($itemname, $description, $folder); + if($item === false) + { + throw new Exception('Create new item failed', MIDAS_INTERNAL_ERROR); + } + $itempolicyuserModel = MidasLoader::loadModel('Itempolicyuser'); + $itempolicyuserModel->createPolicy($userDao, $item, MIDAS_POLICY_ADMIN); + + if(isset($args['itemprivacy'])) + { + $privacyCode = $this->_getValidPrivacyCode($args['itemprivacy']); + } + else + { + // Public by default + $privacyCode = MIDAS_PRIVACY_PUBLIC; + } + $this->_setItemPrivacy($item, $privacyCode); + } + + if(array_key_exists('checksum', $args)) + { + // If we already have a bitstream with this checksum, create a reference and return blank token + $bitstreamModel = MidasLoader::loadModel('Bitstream'); + $existingBitstream = $bitstreamModel->getByChecksum($args['checksum']); + if($existingBitstream) + { + // User must have read access to the existing bitstream if they are circumventing the upload. + // Otherwise an attacker could spoof the checksum and read a private bitstream with a known checksum. + if($itemModel->policyCheck($existingBitstream->getItemrevision()->getItem(), $userDao, MIDAS_POLICY_READ)) + { + $revision = $itemModel->getLastRevision($item); + + if($revision == false) + { + // Create new revision if none exists yet + Zend_Loader::loadClass('ItemRevisionDao', BASE_PATH.'/core/models/dao'); + $revision = new ItemRevisionDao(); + $revision->setChanges('Initial revision'); + $revision->setUser_id($userDao->getKey()); + $revision->setDate(date('c')); + $revision->setLicenseId(null); + $itemModel->addRevision($item, $revision); + } + + $siblings = $revision->getBitstreams(); + foreach($siblings as $sibling) + { + if($sibling->getName() == $args['filename']) + { + // already have a file with this name. don't add new record. + return array('token' => ''); + } + } + Zend_Loader::loadClass('BitstreamDao', BASE_PATH.'/core/models/dao'); + $bitstream = new BitstreamDao(); + $bitstream->setChecksum($args['checksum']); + $bitstream->setName($args['filename']); + $bitstream->setSizebytes($existingBitstream->getSizebytes()); + $bitstream->setPath($existingBitstream->getPath()); + $bitstream->setAssetstoreId($existingBitstream->getAssetstoreId()); + $bitstream->setMimetype($existingBitstream->getMimetype()); + $revisionModel = MidasLoader::loadModel('ItemRevision'); + $revisionModel->addBitstream($revision, $bitstream); + return array('token' => ''); + } + } + } + //we don't already have this content, so create the token + $uploadComponent = MidasLoader::loadComponent('Httpupload'); + $apiSetup = $this->_getApiSetup(); + $uploadComponent->setTestingMode($apiSetup['testing']); + $uploadComponent->setTmpDirectory($apiSetup['tmpDirectory']); + return $uploadComponent->generateToken($args, $userDao->getKey().'/'.$item->getKey()); + } + + /** + * Upload a file to the server. PUT or POST is required. + Will add the file as a bitstream to the item that was specified when + generating the upload token in a new revision to that item, unless + revision param is set. + * @path /system/upload + * @http POST + * @param uploadtoken The upload token (see midas.upload.generatetoken). + * @param filename The name of the bitstream that will be added to the item. + * @param length The length in bytes of the file being uploaded. + * @param mode (Optional) Stream or multipart. Default is stream. + * @param revision (Optional) + If set, will add a new file into the existing passed in revision number. + If set to "head", will add a new file into the most recent revision, + and will create a new revision in this case if none exists. + * @param changes (Optional) + The changes field on the affected item revision, + e.g. for recording what has changed since the previous revision. + * @return The item information of the item created or changed. + */ + function uploadPerform($args) + { + $this->_validateParams($args, array('uploadtoken', 'filename', 'length')); + $request = Zend_Controller_Front::getInstance()->getRequest(); + if(!$request->isPost() && !$request->isPut()) + { + throw new Exception('POST or PUT method required', MIDAS_HTTP_ERROR); + } + + list($userid, $itemid, ) = explode('/', $args['uploadtoken']); + + $itemModel = MidasLoader::loadModel('Item'); + $userModel = MidasLoader::loadModel('User'); + + $userDao = $userModel->load($userid); + if(!$userDao) + { + throw new Exception('Invalid user id from upload token', MIDAS_INVALID_PARAMETER); + } + $item = $itemModel->load($itemid); + + if($item == false) + { + throw new Exception('Unable to find item', MIDAS_INVALID_PARAMETER); + } + if(!$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_WRITE)) + { + throw new Exception('Permission error', MIDAS_INVALID_POLICY); + } + + if(array_key_exists('revision', $args)) + { + if(strtolower($args['revision']) == 'head') + { + $revision = $itemModel->getLastRevision($item); + // if no revision exists, it will be created later + } + else + { + $revision = $itemModel->getRevision($item, $args['revision']); + if($revision == false) + { + throw new Exception('Unable to find revision', MIDAS_INVALID_PARAMETER); + } + } + } + + $mode = array_key_exists('mode', $args) ? $args['mode'] : 'stream'; + + $httpUploadComponent = MidasLoader::loadComponent('Httpupload'); + $apiSetup = $this->_getApiSetup(); + $httpUploadComponent->setTestingMode($apiSetup['testing']); + $httpUploadComponent->setTmpDirectory($apiSetup['tmpDirectory']); + + if(array_key_exists('testingmode', $args)) + { + $httpUploadComponent->setTestingMode(true); + $args['localinput'] = $apiSetup['tmpDirectory'].'/'.$args['filename']; + } + + // Use the Httpupload component to handle the actual file upload + if($mode == 'stream') + { + $result = $httpUploadComponent->process($args); + + $filename = $result['filename']; + $filepath = $result['path']; + $filesize = $result['size']; + $filemd5 = $result['md5']; + } + else if($mode == 'multipart') + { + if(!array_key_exists('file', $args) || !array_key_exists('file', $_FILES)) + { + throw new Exception('Parameter file is not defined', MIDAS_INVALID_PARAMETER); + } + $file = $_FILES['file']; + + $filename = $file['name']; + $filepath = $file['tmp_name']; + $filesize = $file['size']; + $filemd5 = ''; + } + else + { + throw new Exception('Invalid upload mode', MIDAS_INVALID_PARAMETER); + } + + // get the parent folder of this item and notify the callback + // this is made more difficult by the fact the items can have many parents, + // just use the first in the list. + $parentFolders = $item->getFolders(); + if(!isset($parentFolders) || !$parentFolders || sizeof($parentFolders) === 0) + { + // this shouldn't happen with any self-respecting item + throw new Exception('Item does not have a parent folder', MIDAS_INVALID_PARAMETER); + } + $firstParent = $parentFolders[0]; + $validations = Zend_Registry::get('notifier')->callback('CALLBACK_CORE_VALIDATE_UPLOAD', + array('filename' => $filename, + 'size' => $filesize, + 'path' => $filepath, + 'folderId' => $firstParent->getFolderId())); + foreach($validations as $validation) + { + if(!$validation['status']) + { + unlink($filepath); + throw new Exception($validation['message'], MIDAS_INVALID_POLICY); + } + } + $uploadComponent = MidasLoader::loadComponent('Upload'); + $license = null; + $changes = array_key_exists('changes', $args) ? $args['changes'] : ''; + $revisionNumber = null; + if(isset($revision) && $revision !== false) + { + $revisionNumber = $revision->getRevision(); + } + $item = $uploadComponent->createNewRevision($userDao, $filename, $filepath, $changes, $item->getKey(), $revisionNumber, $license, $filemd5); + + if(!$item) + { + throw new Exception('Upload failed', MIDAS_INTERNAL_ERROR); + } + return $item->toArray(); + } + /** * Get the size of a partially completed upload * @path /system/uploadeoffset @@ -574,8 +887,9 @@ function userApikeyDefault($args) function uploadGetoffset($args) { $uploadComponent = MidasLoader::loadComponent('Httpupload'); - $uploadComponent->setTestingMode($this->apiSetup['testing']); - $uploadComponent->setTmpDirectory($this->apiSetup['tmpDirectory']); + $apiSetup = $this->_getApiSetup(); + $uploadComponent->setTestingMode($apiSetup['testing']); + $uploadComponent->setTmpDirectory($apiSetup['tmpDirectory']); return $uploadComponent->getOffset($args); } @@ -861,7 +1175,6 @@ function itemDeletemetadataAll($args) return true; } - /** * Check whether an item with the given name exists in the given folder * @path /item/exists @@ -1479,6 +1792,41 @@ function itemDelete($args) $itemModel->delete($item); } + /** + * Download an item + * @path /item/download/{id} + * @http GET + * @param id The id of the item + * @param revision (Optional) Revision to download. Defaults to latest revision + * @return The bitstream(s) in the item + */ + function itemDownload($args) + { + $this->_validateParams($args, array('id')); + $userDao = $this->_getUser($args); + + $id = $args['id']; + $itemModel = MidasLoader::loadModel('Item'); + $item = $itemModel->load($id); + + if($item === false || !$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_READ)) + { + throw new Exception("This item doesn't exist or you don't have the permissions.", MIDAS_INVALID_POLICY); + } + + $redirUrl = '/download/?items='.$item->getKey(); + if(isset($args['revision'])) + { + $redirUrl .= ','.$args['revision']; + } + if($userDao && array_key_exists('token', $args)) + { + $redirUrl .= '&authToken='.$args['token']; + } + $r = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector'); + $r->setGotoUrl($redirUrl); + } + /** * helper method to validate passed in community privacy status params and * map them to valid community privacy codes. @@ -2358,6 +2706,36 @@ function folderDelete($args) $folderModel->delete($folder); } + /** + * Download a folder + * @path /folder/download/{id} + * @http GET + * @param id The id of the folder + * @return A zip archive of the folder's contents + */ + function folderDownload($args) + { + $this->_validateParams($args, array('id')); + $userDao = $this->_getUser($args); + + $id = $args['id']; + $folderModel = MidasLoader::loadModel('Folder'); + $folder = $folderModel->load($id); + + if($folder === false || !$folderModel->policyCheck($folder, $userDao, MIDAS_POLICY_READ)) + { + throw new Exception("This folder doesn't exist or you don't have the permissions.", MIDAS_INVALID_POLICY); + } + + $redirUrl = '/download/?folders='.$folder->getKey(); + if($userDao && array_key_exists('token', $args)) + { + $redirUrl .= '&authToken='.$args['token']; + } + $r = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector'); + $r->setGotoUrl($redirUrl); + } + /** * Return a list of top level folders belonging to the user * @path /user/folders @@ -2593,6 +2971,94 @@ function bitstreamCount($args) } } + /** + * Download a bitstream either by its id or by a checksum. + */ + function bitstreamDownload($args) + { + if(!array_key_exists('id', $args) && !array_key_exists('checksum', $args)) + { + throw new Exception('Either an id or checksum parameter is required', MIDAS_INVALID_PARAMETER); + } + $userDao = $this->_getUser($args); + + $bitstreamModel = MidasLoader::loadModel('Bitstream'); + $itemModel = MidasLoader::loadModel('Item'); + + if(array_key_exists('id', $args)) + { + $bitstream = $bitstreamModel->load($args['id']); + } + else + { + $bitstreams = $bitstreamModel->getByChecksum($args['checksum'], true); + $bitstream = null; + foreach($bitstreams as $candidate) + { + $rev = $candidate->getItemrevision(); + if(!$rev) + { + continue; + } + $item = $rev->getItem(); + if($itemModel->policyCheck($item, $userDao, MIDAS_POLICY_READ)) + { + $bitstream = $candidate; + break; + } + } + } + + if(!$bitstream) + { + throw new Exception('The bitstream does not exist or you do not have the permissions', MIDAS_INVALID_PARAMETER); + } + + $revision = $bitstream->getItemrevision(); + if(!$revision) + { + throw new Exception('Bitstream does not belong to a revision', MIDAS_INTERNAL_ERROR); + } + + $name = array_key_exists('name', $args) ? $args['name'] : $bitstream->getName(); + $offset = array_key_exists('offset', $args) ? $args['offset'] : '0'; + + $redirUrl = '/download/?bitstream='.$bitstream->getKey().'&offset='.$offset.'&name='.$name; + if($userDao && array_key_exists('token', $args)) + { + $redirUrl .= '&authToken='.$args['token']; + } + $r = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector'); + $r->setGotoUrl($redirUrl); + } + + /** + * Download a bitstream by its id + * @path /bitstream/download/{id} + * @http GET + * @param id The id of the bitstream + * @param name (Optional) Alternate filename to download as + * @param offset (Optional) The download offset in bytes (used for resume) + */ + function bitstreamDownloadById($args) + { + $this->bitstreamDownload($args); + } + + + /** + * Download a bitstream by a checksum. + * @path /bitstream/download + * @http GET + * @param checksum The checksum of the bitstream + * @param name (Optional) Alternate filename to download as + * @param offset (Optional) The download offset in bytes (used for resume) + */ + function bitstreamDownloadByChecksum($args) + { + $this->bitstreamDownload($args); + } + /** * list the users for a group, requires admin privileges on the community * assiated with the group diff --git a/core/views/rest/index/index.phtml b/core/views/rest/index/index.phtml index 461c50470..038af08e1 100644 --- a/core/views/rest/index/index.phtml +++ b/core/views/rest/index/index.phtml @@ -35,11 +35,10 @@ $this->headScript()->appendFile($this->coreWebroot . '/public/js/rest/rest.index
Web API URL = Base URL (serverURL.$this->webroot?>/rest) + individual API URL listed below

- To authenticate requests: You can provide a parameter of token with an authentication token value. -
- Or you can pass a parameter key of useSession to authenticate using the current Midas session. + To authenticate requests, you can provide a parameter of token with an authentication token value obtained by calling /system/login api, + or to authenticate using the current Midas session you can pass a parameter key of useSession.

-
-
+
+
diff --git a/library/REST/Controller/Plugin/RestHandler.php b/library/REST/Controller/Plugin/RestHandler.php index b64666e1e..215671426 100644 --- a/library/REST/Controller/Plugin/RestHandler.php +++ b/library/REST/Controller/Plugin/RestHandler.php @@ -132,13 +132,18 @@ private function handlePathInfo(Zend_Controller_Request_Abstract $request) { $request->setActionName("index"); } + else if(empty($tokens) && ($action == "post" || $action == "put")) + { + $request->setActionName("post"); + } else if(!empty($tokens) && is_numeric($tokens[0])) { $request->setParam('id', array_shift($tokens)); } else { - throw new Exception('The Webapi ' . $request->getPathInfo() . ' is not supported.', -100); + $this->_response->setHttpResponseCode(400); //400 Bad Request + throw new Exception('The Webapi ' . $request->getPathInfo() . ' is not supported.', 400); } } } diff --git a/modules/api/Notification.php b/modules/api/Notification.php index e44e20fb9..2549e9b02 100644 --- a/modules/api/Notification.php +++ b/modules/api/Notification.php @@ -24,6 +24,7 @@ class Api_Notification extends ApiEnabled_Notification { public $moduleName = 'api'; + public $_components = array('Authentication'); public $_moduleComponents = array('Api'); public $_models = array('User'); @@ -85,7 +86,7 @@ public function handleUserDeleted($params) public function tokenAuth($params) { $token = $params['authToken']; - return $this->ModuleComponent->Authentication->getUser(array('token' => $token), null); + return $this->Component->Authentication->getUser(array('token' => $token), null); } } //end class ?> diff --git a/modules/api/controllers/components/ApiComponent.php b/modules/api/controllers/components/ApiComponent.php index 764469076..23d000ccf 100644 --- a/modules/api/controllers/components/ApiComponent.php +++ b/modules/api/controllers/components/ApiComponent.php @@ -245,30 +245,6 @@ function resourceSearch($args) return $searchComponent->searchAll($userDao, $args['search'], $order); } - /** - * helper function to set the privacy code on a passed in item. - */ - protected function _setItemPrivacy($item, $privacyCode) - { - $itempolicygroupModel = MidasLoader::loadModel('Itempolicygroup'); - $groupModel = MidasLoader::loadModel('Group'); - $anonymousGroup = $groupModel->load(MIDAS_GROUP_ANONYMOUS_KEY); - $itempolicygroupDao = $itempolicygroupModel->getPolicy($anonymousGroup, $item); - if($privacyCode == MIDAS_PRIVACY_PRIVATE && $itempolicygroupDao !== false) - { - $itempolicygroupModel->delete($itempolicygroupDao); - } - else if($privacyCode == MIDAS_PRIVACY_PUBLIC && $itempolicygroupDao == false) - { - $itempolicygroupDao = $itempolicygroupModel->createPolicy($anonymousGroup, $item, MIDAS_POLICY_READ); - } - else - { - // ensure the cached privacy status value is up to date - $itempolicygroupModel->computePolicyStatus($item); - } - } - /** * Generate a unique upload token. Either itemid or folderid is required, but both are not allowed. @@ -304,119 +280,7 @@ protected function _setItemPrivacy($item, $privacyCode) */ function uploadGeneratetoken($args) { - $this->_validateParams($args, array('filename')); - if(!array_key_exists('itemid', $args) && !array_key_exists('folderid', $args)) - { - throw new Exception('Parameter itemid or folderid must be defined', MIDAS_INVALID_PARAMETER); - } - if(array_key_exists('itemid', $args) && array_key_exists('folderid', $args)) - { - throw new Exception('Parameter itemid or folderid must be defined, but not both', MIDAS_INVALID_PARAMETER); - } - - $userDao = $this->_getUser($args); - if(!$userDao) - { - throw new Exception('Anonymous users may not upload', MIDAS_INVALID_POLICY); - } - - $itemModel = MidasLoader::loadModel('Item'); - if(array_key_exists('itemid', $args)) - { - $item = $itemModel->load($args['itemid']); - if(!$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_WRITE)) - { - throw new Exception('Invalid policy or itemid', MIDAS_INVALID_POLICY); - } - } - else if(array_key_exists('folderid', $args)) - { - $folderModel = MidasLoader::loadModel('Folder'); - $folder = $folderModel->load($args['folderid']); - if($folder == false) - { - throw new Exception('Parent folder corresponding to folderid doesn\'t exist', MIDAS_INVALID_PARAMETER); - } - if(!$folderModel->policyCheck($folder, $userDao, MIDAS_POLICY_WRITE)) - { - throw new Exception('Invalid policy or folderid', MIDAS_INVALID_POLICY); - } - // create a new item in this folder - $itemname = isset($args['itemname']) ? $args['itemname'] : $args['filename']; - $description = isset($args['itemdescription']) ? $args['itemdescription'] : ''; - $item = $itemModel->createItem($itemname, $description, $folder); - if($item === false) - { - throw new Exception('Create new item failed', MIDAS_INTERNAL_ERROR); - } - $itempolicyuserModel = MidasLoader::loadModel('Itempolicyuser'); - $itempolicyuserModel->createPolicy($userDao, $item, MIDAS_POLICY_ADMIN); - - if(isset($args['itemprivacy'])) - { - $privacyCode = $this->_getValidPrivacyCode($args['itemprivacy']); - } - else - { - // Public by default - $privacyCode = MIDAS_PRIVACY_PUBLIC; - } - $this->_setItemPrivacy($item, $privacyCode); - } - - if(array_key_exists('checksum', $args)) - { - // If we already have a bitstream with this checksum, create a reference and return blank token - $bitstreamModel = MidasLoader::loadModel('Bitstream'); - $existingBitstream = $bitstreamModel->getByChecksum($args['checksum']); - if($existingBitstream) - { - // User must have read access to the existing bitstream if they are circumventing the upload. - // Otherwise an attacker could spoof the checksum and read a private bitstream with a known checksum. - if($itemModel->policyCheck($existingBitstream->getItemrevision()->getItem(), $userDao, MIDAS_POLICY_READ)) - { - $revision = $itemModel->getLastRevision($item); - - if($revision == false) - { - // Create new revision if none exists yet - Zend_Loader::loadClass('ItemRevisionDao', BASE_PATH.'/core/models/dao'); - $revision = new ItemRevisionDao(); - $revision->setChanges('Initial revision'); - $revision->setUser_id($userDao->getKey()); - $revision->setDate(date('c')); - $revision->setLicenseId(null); - $itemModel->addRevision($item, $revision); - } - - $siblings = $revision->getBitstreams(); - foreach($siblings as $sibling) - { - if($sibling->getName() == $args['filename']) - { - // already have a file with this name. don't add new record. - return array('token' => ''); - } - } - Zend_Loader::loadClass('BitstreamDao', BASE_PATH.'/core/models/dao'); - $bitstream = new BitstreamDao(); - $bitstream->setChecksum($args['checksum']); - $bitstream->setName($args['filename']); - $bitstream->setSizebytes($existingBitstream->getSizebytes()); - $bitstream->setPath($existingBitstream->getPath()); - $bitstream->setAssetstoreId($existingBitstream->getAssetstoreId()); - $bitstream->setMimetype($existingBitstream->getMimetype()); - $revisionModel = MidasLoader::loadModel('ItemRevision'); - $revisionModel->addBitstream($revision, $bitstream); - return array('token' => ''); - } - } - } - //we don't already have this content, so create the token - $uploadComponent = MidasLoader::loadComponent('Httpupload'); - $uploadComponent->setTestingMode($this->apiSetup['testing']); - $uploadComponent->setTmpDirectory($this->apiSetup['tmpDirectory']); - return $uploadComponent->generateToken($args, $userDao->getKey().'/'.$item->getKey()); + return $this->_callCoreApiMethod($args, 'uploadGeneratetoken'); } /** @@ -445,132 +309,11 @@ function uploadGetoffset($args) * @param changes (Optional) The changes field on the affected item revision, e.g. for recording what has changed since the previous revision. - * @param return The item information of the item created or changed. + * @return The item information of the item created or changed. */ function uploadPerform($args) { - $this->_validateParams($args, array('uploadtoken', 'filename', 'length')); - if(!$this->controller->getRequest()->isPost() && !$this->controller->getRequest()->isPut()) - { - throw new Exception('POST or PUT method required', MIDAS_HTTP_ERROR); - } - - list($userid, $itemid, ) = explode('/', $args['uploadtoken']); - - $itemModel = MidasLoader::loadModel('Item'); - $userModel = MidasLoader::loadModel('User'); - - $userDao = $userModel->load($userid); - if(!$userDao) - { - throw new Exception('Invalid user id from upload token', MIDAS_INVALID_PARAMETER); - } - $item = $itemModel->load($itemid); - - if($item == false) - { - throw new Exception('Unable to find item', MIDAS_INVALID_PARAMETER); - } - if(!$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_WRITE)) - { - throw new Exception('Permission error', MIDAS_INVALID_POLICY); - } - - if(array_key_exists('revision', $args)) - { - if(strtolower($args['revision']) == 'head') - { - $revision = $itemModel->getLastRevision($item); - // if no revision exists, it will be created later - } - else - { - $revision = $itemModel->getRevision($item, $args['revision']); - if($revision == false) - { - throw new Exception('Unable to find revision', MIDAS_INVALID_PARAMETER); - } - } - } - - $mode = array_key_exists('mode', $args) ? $args['mode'] : 'stream'; - - $httpUploadComponent = MidasLoader::loadComponent('Httpupload'); - $httpUploadComponent->setTestingMode($this->apiSetup['testing']); - $httpUploadComponent->setTmpDirectory($this->apiSetup['tmpDirectory']); - - if(array_key_exists('testingmode', $args)) - { - $httpUploadComponent->setTestingMode(true); - $args['localinput'] = $this->apiSetup['tmpDirectory'].'/'.$args['filename']; - } - - // Use the Httpupload component to handle the actual file upload - if($mode == 'stream') - { - $result = $httpUploadComponent->process($args); - - $filename = $result['filename']; - $filepath = $result['path']; - $filesize = $result['size']; - $filemd5 = $result['md5']; - } - else if($mode == 'multipart') - { - if(!array_key_exists('file', $args) || !array_key_exists('file', $_FILES)) - { - throw new Exception('Parameter file is not defined', MIDAS_INVALID_PARAMETER); - } - $file = $_FILES['file']; - - $filename = $file['name']; - $filepath = $file['tmp_name']; - $filesize = $file['size']; - $filemd5 = ''; - } - else - { - throw new Exception('Invalid upload mode', MIDAS_INVALID_PARAMETER); - } - - // get the parent folder of this item and notify the callback - // this is made more difficult by the fact the items can have many parents, - // just use the first in the list. - $parentFolders = $item->getFolders(); - if(!isset($parentFolders) || !$parentFolders || sizeof($parentFolders) === 0) - { - // this shouldn't happen with any self-respecting item - throw new Exception('Item does not have a parent folder', MIDAS_INVALID_PARAMETER); - } - $firstParent = $parentFolders[0]; - $validations = Zend_Registry::get('notifier')->callback('CALLBACK_CORE_VALIDATE_UPLOAD', - array('filename' => $filename, - 'size' => $filesize, - 'path' => $filepath, - 'folderId' => $firstParent->getFolderId())); - foreach($validations as $validation) - { - if(!$validation['status']) - { - unlink($filepath); - throw new Exception($validation['message'], MIDAS_INVALID_POLICY); - } - } - $uploadComponent = MidasLoader::loadComponent('Upload'); - $license = null; - $changes = array_key_exists('changes', $args) ? $args['changes'] : ''; - $revisionNumber = null; - if(isset($revision) && $revision !== false) - { - $revisionNumber = $revision->getRevision(); - } - $item = $uploadComponent->createNewRevision($userDao, $filename, $filepath, $changes, $item->getKey(), $revisionNumber, $license, $filemd5); - - if(!$item) - { - throw new Exception('Upload failed', MIDAS_INTERNAL_ERROR); - } - return $item->toArray(); + return $this->_callCoreApiMethod($args, 'uploadPerform'); } /** @@ -690,24 +433,7 @@ function folderDelete($args) */ function folderDownload($args) { - $this->_validateParams($args, array('id')); - $userDao = $this->_getUser($args); - - $id = $args['id']; - $folderModel = MidasLoader::loadModel('Folder'); - $folder = $folderModel->load($id); - - if($folder === false || !$folderModel->policyCheck($folder, $userDao, MIDAS_POLICY_READ)) - { - throw new Exception("This folder doesn't exist or you don't have the permissions.", MIDAS_INVALID_POLICY); - } - - $redirUrl = '/download/?folders='.$folder->getKey(); - if($userDao && array_key_exists('token', $args)) - { - $redirUrl .= '&authToken='.$args['token']; - } - $this->controller->redirect($redirUrl); + return $this->_callCoreApiMethod($args, 'folderDownload'); } /** @@ -818,30 +544,6 @@ function folderRemovePolicyuser($args) return $this->_callCoreApiMethod($args, 'folderRemovePolicyuser'); } - /** - * helper method to validate passed in privacy status params and - * map them to valid privacy codes. - * @param string $privacyStatus, should be 'Private' or 'Public' - * @return valid privacy code - */ - private function _getValidPrivacyCode($privacyStatus) - { - if($privacyStatus !== 'Public' && $privacyStatus !== 'Private') - { - throw new Exception('privacy should be one of [Public|Private]', MIDAS_INVALID_PARAMETER); - } - if($privacyStatus === 'Public') - { - $privacyCode = MIDAS_PRIVACY_PUBLIC; - } - else - { - $privacyCode = MIDAS_PRIVACY_PRIVATE; - } - return $privacyCode; - } - - /** * Check whether an item with the given name exists in the given folder * @param parentid The id of the parent folder @@ -894,28 +596,7 @@ function itemGet($args) */ function itemDownload($args) { - $this->_validateParams($args, array('id')); - $userDao = $this->_getUser($args); - - $id = $args['id']; - $itemModel = MidasLoader::loadModel('Item'); - $item = $itemModel->load($id); - - if($item === false || !$itemModel->policyCheck($item, $userDao, MIDAS_POLICY_READ)) - { - throw new Exception("This item doesn't exist or you don't have the permissions.", MIDAS_INVALID_POLICY); - } - - $redirUrl = '/download/?items='.$item->getKey(); - if(isset($args['revision'])) - { - $redirUrl .= ','.$args['revision']; - } - if($userDao && array_key_exists('token', $args)) - { - $redirUrl .= '&authToken='.$args['token']; - } - $this->controller->redirect($redirUrl); + return $this->_callCoreApiMethod($args, 'itemDownload'); } /** @@ -1158,61 +839,7 @@ function userFolders($args) */ function userApikeyDefault($args) { - $this->_validateParams($args, array('email', 'password')); - if(!$this->controller->getRequest()->isPost()) - { - throw new Exception('POST method required', MIDAS_HTTP_ERROR); - } - $email = $args['email']; - $password = $args['password']; - - try - { - $notifications = array(); - $notifications = Zend_Registry::get('notifier')->callback('CALLBACK_CORE_AUTHENTICATION', array( - 'email' => $email, - 'password' => $password)); - } - catch(Zend_Exception $exc) - { - throw new Exception('Login failed', MIDAS_INVALID_PARAMETER); - } - $authModule = false; - foreach($notifications as $module => $user) - { - if($user) - { - $userDao = $user; - $authModule = true; - break; - } - } - - $userModel = MidasLoader::loadModel('User'); - $userApiModel = MidasLoader::loadModel('Userapi'); - if(!$authModule) - { - $userDao = $userModel->getByEmail($email); - if(!$userDao) - { - throw new Exception('Login failed', MIDAS_INVALID_PARAMETER); - } - } - - $instanceSalt = Zend_Registry::get('configGlobal')->password->prefix; - if($authModule || $userModel->hashExists(hash($userDao->getHashAlg(), $instanceSalt.$userDao->getSalt().$password))) - { - if($userDao->getSalt() == '') - { - $passwordHash = $userModel->convertLegacyPasswordHash($userDao, $password); - } - $defaultApiKey = $userApiModel->getByAppAndEmail('Default', $email)->getApikey(); - return array('apikey' => $defaultApiKey); - } - else - { - throw new Exception('Login failed', MIDAS_INVALID_PARAMETER); - } + return $this->_callCoreApiMethod($args, 'userApikeyDefault'); } /** @@ -1261,59 +888,7 @@ function bitstreamGet($args) */ function bitstreamDownload($args) { - if(!array_key_exists('id', $args) && !array_key_exists('checksum', $args)) - { - throw new Exception('Either an id or checksum parameter is required', MIDAS_INVALID_PARAMETER); - } - $userDao = $this->_getUser($args); - - $bitstreamModel = MidasLoader::loadModel('Bitstream'); - $itemModel = MidasLoader::loadModel('Item'); - - if(array_key_exists('id', $args)) - { - $bitstream = $bitstreamModel->load($args['id']); - } - else - { - $bitstreams = $bitstreamModel->getByChecksum($args['checksum'], true); - $bitstream = null; - foreach($bitstreams as $candidate) - { - $rev = $candidate->getItemrevision(); - if(!$rev) - { - continue; - } - $item = $rev->getItem(); - if($itemModel->policyCheck($item, $userDao, MIDAS_POLICY_READ)) - { - $bitstream = $candidate; - break; - } - } - } - - if(!$bitstream) - { - throw new Exception('The bitstream does not exist or you do not have the permissions', MIDAS_INVALID_PARAMETER); - } - - $revision = $bitstream->getItemrevision(); - if(!$revision) - { - throw new Exception('Bitstream does not belong to a revision', MIDAS_INTERNAL_ERROR); - } - - $name = array_key_exists('name', $args) ? $args['name'] : $bitstream->getName(); - $offset = array_key_exists('offset', $args) ? $args['offset'] : '0'; - - $redirUrl = '/download/?bitstream='.$bitstream->getKey().'&offset='.$offset.'&name='.$name; - if($userDao && array_key_exists('token', $args)) - { - $redirUrl .= '&authToken='.$args['token']; - } - $this->controller->redirect($redirUrl); + return $this->_callCoreApiMethod($args, 'bitstreamDownload'); } /** @@ -1464,5 +1039,4 @@ function communityListGroups($args) return $this->_callCoreApiMethod($args, 'communityListGroups'); } - } // end class