public static function postAvatar($args) { \OC_JSON::checkLoggedIn(); \OC_JSON::callCheck(); $user = \OC_User::getUser(); if (isset($_POST['path'])) { $path = stripslashes($_POST['path']); $view = new \OC\Files\View('/' . $user . '/files'); $fileInfo = $view->getFileInfo($path); if ($fileInfo['encrypted'] === true) { $fileName = $view->toTmpFile($path); } else { $fileName = $view->getLocalFile($path); } } elseif (!empty($_FILES)) { $files = $_FILES['files']; if ($files['error'][0] === 0 && is_uploaded_file($files['tmp_name'][0]) && !\OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0])) { \OC\Cache::set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200); $view = new \OC\Files\View('/' . $user . '/cache'); $fileName = $view->getLocalFile('avatar_upload'); unlink($files['tmp_name'][0]); } } else { $l = new \OC_L10n('core'); \OC_JSON::error(array("data" => array("message" => $l->t("No image or file provided")))); return; } try { $image = new \OC_Image(); $image->loadFromFile($fileName); $image->fixOrientation(); if ($image->valid()) { \OC\Cache::set('tmpavatar', $image->data(), 7200); \OC_JSON::error(array("data" => "notsquare")); } else { $l = new \OC_L10n('core'); $mimeType = $image->mimeType(); if ($mimeType !== 'image/jpeg' && $mimeType !== 'image/png') { \OC_JSON::error(array("data" => array("message" => $l->t("Unknown filetype")))); } if (!$image->valid()) { \OC_JSON::error(array("data" => array("message" => $l->t("Invalid image")))); } } } catch (\Exception $e) { \OC_JSON::error(array("data" => array("message" => $e->getMessage()))); } }
/** * Save the certificate and re-generate the certificate bundle * * @param string $certificate the certificate data * @param string $name the filename for the certificate * @return \OCP\ICertificate|void|bool * @throws \Exception If the certificate could not get added */ public function addCertificate($certificate, $name) { if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) { return false; } $dir = $this->getPathToCertificates() . 'uploads/'; if (!file_exists($dir)) { //path might not exist (e.g. non-standard OC_User::getHome() value) //in this case create full path using 3rd (recursive=true) parameter. //note that we use "normal" php filesystem functions here since the certs need to be local mkdir($dir, 0700, true); } try { $file = $dir . $name; $certificateObject = new Certificate($certificate, $name); file_put_contents($file, $certificate); $this->createCertificateBundle(); return $certificateObject; } catch (\Exception $e) { throw $e; } }
/** * Recursive copying of folders * @param string $src source folder * @param string $dest target folder * */ static function copyr($src, $dest) { if (is_dir($src)) { if (!is_dir($dest)) { mkdir($dest); } $files = scandir($src); foreach ($files as $file) { if ($file != "." && $file != "..") { self::copyr("{$src}/{$file}", "{$dest}/{$file}"); } } } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) { copy($src, $dest); } }
/** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @param bool $parentExistsInCache * @return array with metadata of the scanned file */ public function scanFile($file, $reuseExisting = 0, $parentExistsInCache = false) { if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) { $this->emit('\\OC\\Files\\Cache\\Scanner', 'scanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($data) { if ($file and !$parentExistsInCache) { $parent = dirname($file); if ($parent === '.' or $parent === '/') { $parent = ''; } if (!$this->cache->inCache($parent)) { $this->scanFile($parent); } } $newData = $data; $cacheData = $this->cache->get($file); if ($cacheData) { if (isset($cacheData['fileid'])) { $this->permissionsCache->remove($cacheData['fileid']); } if ($reuseExisting) { // prevent empty etag $etag = $cacheData['etag']; $propagateETagChange = false; if (empty($etag)) { $etag = $data['etag']; $propagateETagChange = true; } // only reuse data if the file hasn't explicitly changed if (isset($data['mtime']) && isset($cacheData['mtime']) && $data['mtime'] === $cacheData['mtime']) { if ($reuseExisting & self::REUSE_SIZE && $data['size'] === -1) { $data['size'] = $cacheData['size']; } if ($reuseExisting & self::REUSE_ETAG) { $data['etag'] = $etag; if ($propagateETagChange) { $parent = $file; while ($parent !== '') { $parent = dirname($parent); if ($parent === '.') { $parent = ''; } $parentCacheData = $this->cache->get($parent); $this->cache->update($parentCacheData['fileid'], array('etag' => $this->storage->getETag($parent))); } } } } // Only update metadata that has changed $newData = array_diff_assoc($data, $cacheData); if (isset($newData['etag'])) { $cacheDataString = print_r($cacheData, true); $dataString = print_r($data, true); \OCP\Util::writeLog('OC\\Files\\Cache\\Scanner', "!!! No reuse of etag for '{$file}' !!! \ncache: {$cacheDataString} \ndata: {$dataString}", \OCP\Util::DEBUG); } } } if (!empty($newData)) { $this->cache->put($file, $newData); $this->emit('\\OC\\Files\\Cache\\Scanner', 'postScanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId)); } } else { $this->cache->remove($file); } return $data; } return null; }
/** * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * * @param string $operation * @param string $path * @param array $hooks (optional) * @param mixed $extraParam (optional) * @return mixed * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) { $postFix = substr($path, -1, 1) === '/' ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } $run = $this->runHooks($hooks, $path); list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (!is_null($extraParam)) { $result = $storage->{$operation}($internalPath, $extraParam); } else { $result = $storage->{$operation}($internalPath); } if (in_array('delete', $hooks) and $result) { $this->updater->remove($path); } if (in_array('write', $hooks) and $operation !== 'fopen') { $this->updater->update($path); } if (in_array('touch', $hooks)) { $this->updater->update($path, $extraParam); } if ($this->shouldEmitHooks($path) && $result !== false) { if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open $this->runHooks($hooks, $path, true); } } return $result; } } return null; }
/** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @param int $parentId * @param array | null $cacheData existing data in the cache for the file to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning * @return array an array of metadata of the scanned file * @throws \OC\ServerNotAvailableException * @throws \OCP\Lock\LockedException */ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) { if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) { if ($lock) { $this->storage->acquireLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } $this->emit('\\OC\\Files\\Cache\\Scanner', 'scanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($data) { $parent = dirname($file); if ($parent === '.' or $parent === '/') { $parent = ''; } if ($parentId === -1) { $parentId = $this->cache->getId($parent); } // scan the parent if it's not in the cache (id -1) and the current file is not the root folder if ($file and $parentId === -1) { $parentData = $this->scanFile($parent); $parentId = $parentData['fileid']; } if ($parent) { $data['parent'] = $parentId; } if (is_null($cacheData)) { $cacheData = $this->cache->get($file); } if ($cacheData and $reuseExisting and isset($cacheData['fileid'])) { // prevent empty etag if (empty($cacheData['etag'])) { $etag = $data['etag']; } else { $etag = $cacheData['etag']; } $fileId = $cacheData['fileid']; $data['fileid'] = $fileId; // only reuse data if the file hasn't explicitly changed if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) { $data['mtime'] = $cacheData['mtime']; if ($reuseExisting & self::REUSE_SIZE && $data['size'] === -1) { $data['size'] = $cacheData['size']; } if ($reuseExisting & self::REUSE_ETAG) { $data['etag'] = $etag; } } // Only update metadata that has changed $newData = array_diff_assoc($data, $cacheData); } else { $newData = $data; $fileId = -1; } if (!empty($newData)) { $data['fileid'] = $this->addToCache($file, $newData, $fileId); } $this->emit('\\OC\\Files\\Cache\\Scanner', 'postScanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId)); } else { $this->removeFromCache($file); } if ($lock) { $this->storage->releaseLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } return $data; } return null; }
/** * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * * @param string $operation * @param string $path * @param array $hooks (optional) * @param mixed $extraParam (optional) * @return mixed * @throws \Exception * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { $postFix = substr($path, -1, 1) === '/' ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { // always a shared lock during pre-hooks so the hook can read the file $this->lockFile($path, ILockingProvider::LOCK_SHARED); } $run = $this->runHooks($hooks, $path); /** @var \OC\Files\Storage\Storage $storage */ list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); } try { if (!is_null($extraParam)) { $result = $storage->{$operation}($internalPath, $extraParam); } else { $result = $storage->{$operation}($internalPath); } } catch (\Exception $e) { if (in_array('write', $hooks) || in_array('delete', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); } else { if (in_array('read', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } throw $e; } if (in_array('delete', $hooks) and $result) { $this->removeUpdate($storage, $internalPath); } if (in_array('write', $hooks) and $operation !== 'fopen') { $this->writeUpdate($storage, $internalPath); } if (in_array('touch', $hooks)) { $this->writeUpdate($storage, $internalPath, $extraParam); } if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { $this->changeLock($path, ILockingProvider::LOCK_SHARED); } $unlockLater = false; if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { $unlockLater = true; $result = CallbackWrapper::wrap($result, null, null, function () use($hooks, $path) { if (in_array('write', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); } else { if (in_array('read', $hooks)) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } }); } if ($this->shouldEmitHooks($path) && $result !== false) { if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open $this->runHooks($hooks, $path, true); } } if (!$unlockLater && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks))) { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } return $result; } else { $this->unlockFile($path, ILockingProvider::LOCK_SHARED); } } return null; }
/** * @NoAdminRequired * * @param string $path * @return DataResponse */ public function postAvatar($path) { $userId = $this->userSession->getUser()->getUID(); $files = $this->request->getUploadedFile('files'); $headers = []; if ($this->request->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE_8])) { // due to upload iframe workaround, need to set content-type to text/plain $headers['Content-Type'] = 'text/plain'; } if (isset($path)) { $path = stripslashes($path); $node = $this->userFolder->get($path); if (!$node instanceof \OCP\Files\File) { return new DataResponse(['data' => ['message' => $this->l->t('Please select a file.')]], Http::STATUS_OK, $headers); } if ($node->getSize() > 20 * 1024 * 1024) { return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]], Http::STATUS_BAD_REQUEST, $headers); } $content = $node->getContent(); } elseif (!is_null($files)) { if ($files['error'][0] === 0 && is_uploaded_file($files['tmp_name'][0]) && !\OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0])) { if ($files['size'][0] > 20 * 1024 * 1024) { return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]], Http::STATUS_BAD_REQUEST, $headers); } $this->cache->set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200); $content = $this->cache->get('avatar_upload'); unlink($files['tmp_name'][0]); } else { return new DataResponse(['data' => ['message' => $this->l->t('Invalid file provided')]], Http::STATUS_BAD_REQUEST, $headers); } } else { //Add imgfile return new DataResponse(['data' => ['message' => $this->l->t('No image or file provided')]], Http::STATUS_BAD_REQUEST, $headers); } try { $image = new \OC_Image(); $image->loadFromData($content); $image->fixOrientation(); if ($image->valid()) { $mimeType = $image->mimeType(); if ($mimeType !== 'image/jpeg' && $mimeType !== 'image/png') { return new DataResponse(['data' => ['message' => $this->l->t('Unknown filetype')]], Http::STATUS_OK, $headers); } $this->cache->set('tmpAvatar', $image->data(), 7200); return new DataResponse(['data' => 'notsquare'], Http::STATUS_OK, $headers); } else { return new DataResponse(['data' => ['message' => $this->l->t('Invalid image')]], Http::STATUS_OK, $headers); } } catch (\Exception $e) { $this->logger->logException($e, ['app' => 'core']); return new DataResponse(['data' => ['message' => $this->l->t('An error occurred. Please contact your admin.')]], Http::STATUS_OK, $headers); } }
/** * Modifies a string to remove all non ASCII characters and spaces. * * @param string $text * @return string */ private function slugify($text) { $originalText = $text; // replace non letter or digits or dots by - $text = preg_replace('~[^\\pL\\d\\.]+~u', '-', $text); // trim $text = trim($text, '-'); // transliterate if (function_exists('iconv')) { $text = iconv('utf-8', 'us-ascii//TRANSLIT//IGNORE', $text); } // lowercase $text = strtolower($text); // remove unwanted characters $text = preg_replace('~[^-\\w\\.]+~', '', $text); // trim ending dots (for security reasons and win compatibility) $text = preg_replace('~\\.+$~', '', $text); if (empty($text) || \OC\Files\Filesystem::isFileBlacklisted($text)) { /** * Item slug would be empty. Previously we used uniqid() here. * However this means that the behaviour is not reproducible, so * when uploading files into a "empty" folder, the folders name is * different. * * The other case is, that the slugified name would be a blacklisted * filename. In this case we just use the same workaround by * returning the secure md5 hash of the original name. * * * If there would be a md5() hash collision, the deduplicate check * will spot this and append an index later, so this should not be * a problem. */ return md5($originalText); } return $text; }
/** * @dataProvider isFileBlacklistedData */ public function testIsFileBlacklisted($path, $expected) { $this->assertSame($expected, \OC\Files\Filesystem::isFileBlacklisted($path)); }
/** * @NoAdminRequired */ public function start() { $request = $this->request; $response = new JSONResponse(); $params = $this->request->urlParams; $app = new App($this->api->getUserId()); $addressBook = $app->getAddressBook($params['backend'], $params['addressBookId']); if (!$addressBook->hasPermission(\OCP\PERMISSION_CREATE)) { $response->setStatus('403'); $response->bailOut(App::$l10n->t('You do not have permissions to import into this address book.')); return $response; } $filename = isset($request->post['filename']) ? $request->post['filename'] : null; $progresskey = isset($request->post['progresskey']) ? $request->post['progresskey'] : null; if (is_null($filename)) { $response->bailOut(App::$l10n->t('File name missing from request.')); return $response; } if (is_null($progresskey)) { $response->bailOut(App::$l10n->t('Progress key missing from request.')); return $response; } $filename = strtr($filename, array('/' => '', "\\" => '')); if (\OC\Files\Filesystem::isFileBlacklisted($filename)) { $response->bailOut(App::$l10n->t('Attempt to access blacklisted file:') . $filename); return $response; } $view = \OCP\Files::getStorage('contacts'); $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $file = $view->file_get_contents('/imports/' . $filename); \OC_FileProxy::$enabled = $proxyStatus; $writeProgress = function ($pct) use($progresskey) { \OC_Cache::set($progresskey, $pct, 300); }; $cleanup = function () use($view, $filename, $progresskey) { if (!$view->unlink('/imports/' . $filename)) { $response->debug('Unable to unlink /imports/' . $filename); } \OC_Cache::remove($progresskey); }; $writeProgress('20'); $nl = "\n"; $file = str_replace(array("\r", "\n\n"), array("\n", "\n"), $file); $lines = explode($nl, $file); $inelement = false; $parts = array(); $card = array(); foreach ($lines as $line) { if (strtoupper(trim($line)) == 'BEGIN:VCARD') { $inelement = true; } elseif (strtoupper(trim($line)) == 'END:VCARD') { $card[] = $line; $parts[] = implode($nl, $card); $card = array(); $inelement = false; } if ($inelement === true && trim($line) != '') { $card[] = $line; } } if (count($parts) === 0) { $response->bailOut(App::$l10n->t('No contacts found in: ') . $filename); $cleanup(); return $response; } //import the contacts $imported = 0; $failed = 0; $partially = 0; $processed = 0; // TODO: Add a new group: "Imported at {date}" foreach ($parts as $part) { try { $vcard = VObject\Reader::read($part); } catch (VObject\ParseException $e) { try { $vcard = VObject\Reader::read($part, VObject\Reader::OPTION_IGNORE_INVALID_LINES); $partially += 1; $response->debug('Import: Retrying reading card. Error parsing VCard: ' . $e->getMessage()); } catch (\Exception $e) { $failed += 1; $response->debug('Import: skipping card. Error parsing VCard: ' . $e->getMessage()); continue; // Ditch cards that can't be parsed by Sabre. } } try { $vcard->validate(MyVCard::REPAIR | MyVCard::UPGRADE); } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' ' . 'Error validating vcard: ' . $e->getMessage(), \OCP\Util::ERROR); $failed += 1; } /** * TODO * - Check if a contact with identical UID exists. * - If so, fetch that contact and call $contact->mergeFromVCard($vcard); * - Increment $updated var (not present yet.) * - continue */ try { if ($addressBook->addChild($vcard)) { $imported += 1; } else { $failed += 1; } } catch (\Exception $e) { $response->debug('Error importing vcard: ' . $e->getMessage() . $nl . $vcard->serialize()); $failed += 1; } $processed += 1; $writeProgress($processed); } //done the import sleep(3); // Give client side a chance to read the progress. $response->setParams(array('backend' => $params['backend'], 'addressBookId' => $params['addressBookId'], 'imported' => $imported, 'partially' => $partially, 'failed' => $failed)); return $response; }
/** * @NoAdminRequired */ public function start() { $request = $this->request; $response = new JSONResponse(); $params = $this->request->urlParams; $app = new App(\OCP\User::getUser()); $addressBookId = $params['addressBookId']; $format = $params['importType']; $addressBook = $app->getAddressBook($params['backend'], $addressBookId); if (!$addressBook->hasPermission(\OCP\PERMISSION_CREATE)) { $response->setStatus('403'); $response->bailOut(App::$l10n->t('You do not have permissions to import into this address book.')); return $response; } $filename = isset($request->post['filename']) ? $request->post['filename'] : null; $progresskey = isset($request->post['progresskey']) ? $request->post['progresskey'] : null; if (is_null($filename)) { $response->bailOut(App::$l10n->t('File name missing from request.')); return $response; } if (is_null($progresskey)) { $response->bailOut(App::$l10n->t('Progress key missing from request.')); return $response; } $filename = strtr($filename, array('/' => '', "\\" => '')); if (\OC\Files\Filesystem::isFileBlacklisted($filename)) { $response->bailOut(App::$l10n->t('Attempt to access blacklisted file:') . $filename); return $response; } $view = \OCP\Files::getStorage('contacts'); $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $file = $view->file_get_contents('/imports/' . $filename); \OC_FileProxy::$enabled = $proxyStatus; $importManager = new ImportManager(); $formatList = $importManager->getTypes(); $found = false; $parts = array(); foreach ($formatList as $formatName => $formatDisplayName) { if ($formatName == $format) { $parts = $importManager->importFile($view->getLocalFile('/imports/' . $filename), $formatName); $found = true; } } if (!$found) { // detect file type $mostLikelyName = ""; $mostLikelyValue = 0; $probability = $importManager->detectFileType($view->getLocalFile('/imports/' . $filename)); foreach ($probability as $probName => $probValue) { if ($probValue > $mostLikelyValue) { $mostLikelyName = $probName; $mostLikelyValue = $probValue; } } if ($mostLikelyValue > 0) { // found one (most likely...) $parts = $importManager->importFile($view->getLocalFile('/imports/' . $filename), $mostLikelyName); } } if ($parts) { //import the contacts $imported = 0; $failed = 0; $processed = 0; $total = count($parts); foreach ($parts as $part) { /** * TODO * - Check if a contact with identical UID exists. * - If so, fetch that contact and call $contact->mergeFromVCard($part); * - Increment $updated var (not present yet.) * - continue */ try { $id = $addressBook->addChild($part); if ($id) { $imported++; $favourites = $part->select('X-FAVOURITES'); foreach ($favourites as $favourite) { if ($favourite->getValue() == 'yes') { $this->tagMgr->addToFavorites($id); } } } else { $failed++; } } catch (\Exception $e) { $response->debug('Error importing vcard: ' . $e->getMessage() . $nl . $part->serialize()); $failed++; } $processed++; $this->writeProcess($processed, $total, $progresskey); } } else { $imported = 0; $failed = 0; $processed = 0; $total = 0; } $this->cleanup($view, $filename, $progresskey, $response); //done the import sleep(3); // Give client side a chance to read the progress. $response->setParams(array('backend' => $params['backend'], 'addressBookId' => $params['addressBookId'], 'importType' => $params['importType'], 'imported' => $imported, 'count' => $processed, 'total' => $total, 'failed' => $failed)); return $response; }
/** * Modifies a string to remove all non ASCII characters and spaces. * * @param string $text * @return string */ private function slugify($text) { // replace non letter or digits or dots by - $text = preg_replace('~[^\\pL\\d\\.]+~u', '-', $text); // trim $text = trim($text, '-'); // transliterate if (function_exists('iconv')) { $text = iconv('utf-8', 'us-ascii//TRANSLIT//IGNORE', $text); } // lowercase $text = strtolower($text); // remove unwanted characters $text = preg_replace('~[^-\\w\\.]+~', '', $text); // trim ending dots (for security reasons and win compatibility) $text = preg_replace('~\\.+$~', '', $text); if (empty($text) || \OC\Files\Filesystem::isFileBlacklisted($text)) { return uniqid(); } return $text; }
/** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @param int $parentId * @param array | null $cacheData existing data in the cache for the file to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning * @return array an array of metadata of the scanned file * @throws \OC\ServerNotAvailableException * @throws \OCP\Lock\LockedException */ public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) { // only proceed if $file is not a partial file nor a blacklisted file if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) { //acquire a lock if ($lock) { if ($this->storage->instanceOfStorage('\\OCP\\Files\\Storage\\ILockingStorage')) { $this->storage->acquireLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } } $data = $this->getData($file); if ($data) { // pre-emit only if it was a file. By that we avoid counting/treating folders as files if ($data['mimetype'] !== 'httpd/unix-directory') { $this->emit('\\OC\\Files\\Cache\\Scanner', 'scanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); } $parent = dirname($file); if ($parent === '.' or $parent === '/') { $parent = ''; } if ($parentId === -1) { $parentId = $this->cache->getId($parent); } // scan the parent if it's not in the cache (id -1) and the current file is not the root folder if ($file and $parentId === -1) { $parentData = $this->scanFile($parent); $parentId = $parentData['fileid']; } if ($parent) { $data['parent'] = $parentId; } if (is_null($cacheData)) { /** @var CacheEntry $cacheData */ $cacheData = $this->cache->get($file); } if ($cacheData and $reuseExisting and isset($cacheData['fileid'])) { // prevent empty etag if (empty($cacheData['etag'])) { $etag = $data['etag']; } else { $etag = $cacheData['etag']; } $fileId = $cacheData['fileid']; $data['fileid'] = $fileId; // only reuse data if the file hasn't explicitly changed if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) { $data['mtime'] = $cacheData['mtime']; if ($reuseExisting & self::REUSE_SIZE && $data['size'] === -1) { $data['size'] = $cacheData['size']; } if ($reuseExisting & self::REUSE_ETAG) { $data['etag'] = $etag; } } // Only update metadata that has changed $newData = array_diff_assoc($data, $cacheData->getData()); } else { $newData = $data; $fileId = -1; } if (!empty($newData)) { // Reset the checksum if the data has changed $newData['checksum'] = ''; $data['fileid'] = $this->addToCache($file, $newData, $fileId); } if (isset($cacheData['size'])) { $data['oldSize'] = $cacheData['size']; } else { $data['oldSize'] = 0; } if (isset($cacheData['encrypted'])) { $data['encrypted'] = $cacheData['encrypted']; } // post-emit only if it was a file. By that we avoid counting/treating folders as files if ($data['mimetype'] !== 'httpd/unix-directory') { $this->emit('\\OC\\Files\\Cache\\Scanner', 'postScanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId)); } } else { $this->removeFromCache($file); } //release the acquired lock if ($lock) { if ($this->storage->instanceOfStorage('\\OCP\\Files\\Storage\\ILockingStorage')) { $this->storage->releaseLock($file, ILockingProvider::LOCK_SHARED, $this->lockingProvider); } } if ($data && !isset($data['encrypted'])) { $data['encrypted'] = false; } return $data; } return null; }
/** * Save the certificate and re-generate the certificate bundle * * @param string $certificate the certificate data * @param string $name the filename for the certificate * @return \OCP\ICertificate * @throws \Exception If the certificate could not get added */ public function addCertificate($certificate, $name) { if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) { throw new \Exception('Filename is not valid'); } $dir = $this->getPathToCertificates() . 'uploads/'; if (!$this->view->file_exists($dir)) { $this->view->mkdir($dir); } try { $file = $dir . $name; $certificateObject = new Certificate($certificate, $name); $this->view->file_put_contents($file, $certificate); $this->createCertificateBundle(); return $certificateObject; } catch (\Exception $e) { throw $e; } }
/** * scan a single file and store it in the cache * * @param string $file * @param int $reuseExisting * @return array an array of metadata of the scanned file */ public function scanFile($file, $reuseExisting = 0) { if (!self::isPartialFile($file) and !Filesystem::isFileBlacklisted($file)) { $this->emit('\\OC\\Files\\Cache\\Scanner', 'scanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($data) { $parent = dirname($file); if ($parent === '.' or $parent === '/') { $parent = ''; } $parentId = $this->cache->getId($parent); // scan the parent if it's not in the cache (id -1) and the current file is not the root folder if ($file and $parentId === -1) { $parentData = $this->scanFile($parent); $parentId = $parentData['fileid']; } if ($parent) { $data['parent'] = $parentId; } $cacheData = $this->cache->get($file); if ($cacheData and $reuseExisting) { // prevent empty etag if (empty($cacheData['etag'])) { $etag = $data['etag']; } else { $etag = $cacheData['etag']; } // only reuse data if the file hasn't explicitly changed if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) { $data['mtime'] = $cacheData['mtime']; if ($reuseExisting & self::REUSE_SIZE && $data['size'] === -1) { $data['size'] = $cacheData['size']; } if ($reuseExisting & self::REUSE_ETAG) { $data['etag'] = $etag; } } // Only update metadata that has changed $newData = array_diff_assoc($data, $cacheData); if (isset($newData['etag'])) { $cacheDataString = print_r($cacheData, true); $dataString = print_r($data, true); \OCP\Util::writeLog('OC\\Files\\Cache\\Scanner', "!!! No reuse of etag for '{$file}' !!! \ncache: {$cacheDataString} \ndata: {$dataString}", \OCP\Util::DEBUG); } } else { $newData = $data; } if (!empty($newData)) { $data['fileid'] = $this->addToCache($file, $newData); $this->emit('\\OC\\Files\\Cache\\Scanner', 'postScanFile', array($file, $this->storageId)); \OC_Hook::emit('\\OC\\Files\\Cache\\Scanner', 'post_scan_file', array('path' => $file, 'storage' => $this->storageId)); } } else { $this->removeFromCache($file); } return $data; } return null; }
/** * @NoAdminRequired * * @param string $path * @return DataResponse */ public function postAvatar($path) { $userId = $this->userSession->getUser()->getUID(); $files = $this->request->getUploadedFile('files'); if (isset($path)) { $path = stripslashes($path); $view = new \OC\Files\View('/' . $userId . '/files'); if ($view->filesize($path) > 20 * 1024 * 1024) { return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]], Http::STATUS_BAD_REQUEST); } $fileName = $view->getLocalFile($path); } elseif (!is_null($files)) { if ($files['error'][0] === 0 && is_uploaded_file($files['tmp_name'][0]) && !\OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0])) { if ($files['size'][0] > 20 * 1024 * 1024) { return new DataResponse(['data' => ['message' => $this->l->t('File is too big')]], Http::STATUS_BAD_REQUEST); } $this->cache->set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200); $view = new \OC\Files\View('/' . $userId . '/cache'); $fileName = $view->getLocalFile('avatar_upload'); unlink($files['tmp_name'][0]); } else { return new DataResponse(['data' => ['message' => $this->l->t('Invalid file provided')]], Http::STATUS_BAD_REQUEST); } } else { //Add imgfile return new DataResponse(['data' => ['message' => $this->l->t('No image or file provided')]], Http::STATUS_BAD_REQUEST); } try { $image = new \OC_Image(); $image->loadFromFile($fileName); $image->fixOrientation(); if ($image->valid()) { $mimeType = $image->mimeType(); if ($mimeType !== 'image/jpeg' && $mimeType !== 'image/png') { return new DataResponse(['data' => ['message' => $this->l->t('Unknown filetype')]]); } $this->cache->set('tmpAvatar', $image->data(), 7200); return new DataResponse(['data' => 'notsquare']); } else { return new DataResponse(['data' => ['message' => $this->l->t('Invalid image')]]); } } catch (\Exception $e) { return new DataResponse(['data' => ['message' => $e->getMessage()]]); } }
/** * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * * @param string $operation * @param string $path * @param array $hooks (optional) * @param mixed $extraParam (optional) * @return mixed * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); if (\OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and Filesystem::isValidPath($path) and !Filesystem::isFileBlacklisted($path) ) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } $run = $this->runHooks($hooks, $path); list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); } else { $result = $storage->$operation($internalPath); } $result = \OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); if ($this->shouldEmitHooks($path) && $result !== false) { if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open $this->runHooks($hooks, $path, true); } } return $result; } } return null; }