diff --git a/ktwebservice/KTUploadManager.inc.php b/ktwebservice/KTUploadManager.inc.php index f88fcb0..be50f93 100644 --- a/ktwebservice/KTUploadManager.inc.php +++ b/ktwebservice/KTUploadManager.inc.php @@ -134,6 +134,36 @@ class KTUploadManager return $tempfilename; } + + /** + * + * @param string $content file content NOT base64 encoded (may be string, may be binary) + * @param string $prefix [optional] + * @return $tempfilename the name of the temporary file created + */ + function store_file($content, $prefix= 'sa_') + { + $tempfilename = $this->get_temp_filename($prefix); + if (!is_writable($tempfilename)) + { + return new PEAR_Error("Cannot write to file: $tempfilename"); + } + + if (!$this->is_valid_temporary_file($tempfilename)) + { + return new PEAR_Error("Invalid temporary file: $tempfilename. There is a problem with the temporary storage path: $this->temp_dir."); + } + + $fp=fopen($tempfilename, 'wb'); + if ($fp === false) + { + return new PEAR_Error("Cannot write content to temporary file: $tempfilename."); + } + fwrite($fp, $content); + fclose($fp); + + return $tempfilename; + } /** * This tells the manager to manage a file that has been uploaded. diff --git a/lib/api/ktcmis/services/CMISObjectService.inc.php b/lib/api/ktcmis/services/CMISObjectService.inc.php index 664216b..372a9c9 100644 --- a/lib/api/ktcmis/services/CMISObjectService.inc.php +++ b/lib/api/ktcmis/services/CMISObjectService.inc.php @@ -80,16 +80,16 @@ class CMISObjectService { * @param array $properties Array of properties which must be applied to the created document object * @param string $folderId The id of the folder which will be the parent of the created document object * This parameter is optional IF unfilingCapability is supported - * @param contentStream $contentStream optional content stream data + * @param string $contentStream optional content stream data - expected as a base64 encoded string * @param string $versioningState optional version state value: checkedout/major/minor * @return string $objectId The id of the created folder object */ // TODO throw ConstraintViolationException if: // value of any of the properties violates the min/max/required/length constraints - // specified in the property definition in the Object-Type. + // specified in the property definition in the Object-Type. function createDocument($repositoryId, $typeId, $properties, $folderId = null, $contentStream = null, $versioningState = null) - { + { $objectId = null; // fetch type definition of supplied type and check for base type "document", if not true throw exception @@ -196,11 +196,17 @@ class CMISObjectService { // this check isn't strictly necessary; however it is needed for a repository which does not support content streams if (!empty($contentStream)) { + // TODO consider checking whether content is encoded (currently we expect encoded) + // TODO choose between this and the alternative decode function (see CMISUtil class) + // The current one appears to be miles better (1/0/3 vs 14/4/57 on respective test files) + $contentStream = CMISUtil::decodeChunkedContentStream($contentStream); + // NOTE There is a function in CMISUtil to do this, written for the unit tests but since KTUploadManager exists // and has more functionality which could come in useful at some point I decided to go with that instead // (did not know this existed when I wrote the CMISUtil function) $uploadManager = new KTUploadManager(); - $tempfilename = $uploadManager->store_base64_file($contentStream, 'cmis_'); + // assumes already decoded from base64, should use store_base64_file if not + $tempfilename = $uploadManager->store_file($contentStream, 'cmis_'); // metadata $metadata = array(); diff --git a/lib/api/ktcmis/util/CMISUtil.inc.php b/lib/api/ktcmis/util/CMISUtil.inc.php index fe5ab69..81f4285 100644 --- a/lib/api/ktcmis/util/CMISUtil.inc.php +++ b/lib/api/ktcmis/util/CMISUtil.inc.php @@ -298,10 +298,6 @@ class CMISUtil { $object['properties']['ObjectId'] = array('type' => $properties->getFieldType('ObjectId'), 'value' => $properties->getValue('ObjectId')); - - - - if (strtolower($properties->getValue('ObjectTypeId')) == 'document') { @@ -400,6 +396,117 @@ class CMISUtil { return $temp; } + + // TODO run evaluations on each of the following two functions and determine which + // is generally more efficienct + + /** + * Alternative function for decoding chunked streams, this will decode in blocks of 4. + * Not sure which method is more efficient, this or the function below (this does not + * re-encode but I am working on removing that step for the other function.) + * + * NOTE The current one appears to be much slower (14/4/57 vs 1/0/3 on respective test files) + * + * @param string $contentStream the base64 encoded content stream + * @return string $decoded the decoded content stream + */ + static public function decodeContentStream($contentStream) + { + $decoded = ''; + + $contentStream = preg_replace('/\r?\n+/', '', $contentStream); + + // decode in chunks or 4 chars at a time + for($i = 0, $len = strlen($contentStream); $i < $len; $i += 4) { + $decoded .= base64_decode(substr($contentStream, $i, 4)); + } + + return $decoded; + } + + /** + * Checks the contentStream and ensures that it is a correct base64 string; + * This is purely for clients such as CMISSpaces breaking the content into + * chunks before base64 encoding. + * + * If the stream is chunked, it is decoded in chunks and sent back as a single stream. + * If it is not chunked it is decoded as is and sent back as a single stream. + * + * NOTE this function and the above need to be checked for efficiency. + * The current one appears to be miles better (1/0/3 vs 14/4/57 on respective test files) + * + * @param object $contentStream + * @return string decoded + */ + static public function decodeChunkedContentStream($contentStream) + { + // check the content stream for any lines of unusual length (except the last line, which can be any length) + $count = -1; + $length = 0; + $b64String = ''; + $outputStream = ''; + $decode = array(); + $chunks = 1; + $decoded = ''; + $chunked = ''; + + $splitStream = explode("\n", $contentStream); + foreach ($splitStream as $line) + { + $curlen = strlen($line); + + if ($length == 0) { + $length = $curlen; + } + + // if we find one we know that we must split the line here and end the previous base64 string + if ($curlen > $length) + { + // check for a new chunk + // either we have an equals sign (or two) + if (preg_match('/([^=]*={0,2})(.*)/', $line, $matches)) + { + $lastChunk = $matches[1]; + $nextChunk = $matches[2]; + } + // or we need to try by line length + else { + $lastChunk = substr($line, 0, $curlen - $length); + $nextChunk = substr($line, $curlen - $length); + } + + $decode[++$count] = $b64String . $lastChunk; + + $b64String = $nextChunk . "\n"; + $length = strlen($nextChunk); + + ++$chunks; + } + else { + $b64String .= $line . "\n"; + } + } + + // anything left over + if (!empty($b64String)) { + $decode[] = $b64String; + } + + if ($chunks > 1) + { + foreach($decode as $code) { + // decode, append to output to be re-encoded + $chunked .= base64_decode($code); + } + + $decoded = $chunked; + } + else { + $decoded = base64_decode($decode[0]); + } + + return $decoded; + } } diff --git a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php index 95f3869..5c6a965 100644 --- a/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php +++ b/webservice/atompub/cmis/KT_cmis_atom_server.services.inc.php @@ -103,10 +103,17 @@ class KT_cmis_atom_service_folder extends KT_atom_service { // determine whether this is a folder or a document create // document create will have a content tag or containing base64 encoding of the document $content = KT_cmis_atom_service_helper::getAtomValues($this->parsedXMLContent['@children'], 'content'); - if (is_null($content)) + + // check content for weird chars + $matches = array(); + preg_match('/[^\w\d\/\+\n]*/', $content, $matches); + + if (is_null($content)) { $type = 'folder'; - else + } + else { $type = 'document'; + } $cmisObjectProperties = KT_cmis_atom_service_helper::getCmisProperties($this->parsedXMLContent['@children']['cmis:object'] [0]['@children']['cmis:properties'] @@ -124,20 +131,17 @@ class KT_cmis_atom_service_folder extends KT_atom_service { if ($typeId != 'Unknown') { + /*$f = fopen('c:\kt-stuff\here.txt', 'w'); + fwrite($f, 'fgfgfgfg'); + fclose($f);*/ $this->setStatus(self::STATUS_CREATED); -// if ($type == 'folder') -// { - $feed = KT_cmis_atom_service_helper::getObjectFeed($ObjectService, $repositoryId, $newObjectId, 'POST'); -// } -// else -// { -// $NavigationService = new NavigationService(KT_cmis_atom_service_helper::getKt()); -// $cmisEntry = $ObjectService->getProperties($repositoryId, $folderId, false, false); -// $feed = $this->getFolderChildrenFeed($NavigationService, $repositoryId, $folderId, $cmisEntry['properties']['Name']['value'], 'POST'); -// } + $feed = KT_cmis_atom_service_helper::getObjectFeed($ObjectService, $repositoryId, $newObjectId, 'POST'); } else { + /*$f = fopen('c:\kt-stuff\failed.txt', 'w'); + fwrite($f, 'fgfgfgfg'); + fclose($f);*/ $feed = KT_cmis_atom_service_helper::getErrorFeed($this, self::STATUS_SERVER_ERROR, $newObjectId['message']); } diff --git a/webservice/atompub/cmis/index.php b/webservice/atompub/cmis/index.php index 9584406..a6dc97b 100644 --- a/webservice/atompub/cmis/index.php +++ b/webservice/atompub/cmis/index.php @@ -72,11 +72,6 @@ if(!KT_atom_HTTPauth::isLoggedIn()) { KT_atom_HTTPauth::login('KnowledgeTree DMS', 'You must authenticate to enter this realm'); } -//$username = $_SERVER['PHP_AUTH_USER']; -//$password = $_SERVER['PHP_AUTH_PW']; -//// fetch user name and password (check auth include for proper method) -//KT_cmis_atom_service_helper::login($username, $password); - //Start the AtomPubProtocol Routing Engine $APP = new KT_cmis_atom_server();