aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/TempManager.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/TempManager.php')
-rw-r--r--lib/private/TempManager.php221
1 files changed, 221 insertions, 0 deletions
diff --git a/lib/private/TempManager.php b/lib/private/TempManager.php
new file mode 100644
index 00000000000..4c0ffcf43d7
--- /dev/null
+++ b/lib/private/TempManager.php
@@ -0,0 +1,221 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OC;
+
+use bantu\IniGetWrapper\IniGetWrapper;
+use OCP\Files;
+use OCP\IConfig;
+use OCP\ITempManager;
+use OCP\Security\ISecureRandom;
+use Psr\Log\LoggerInterface;
+
+class TempManager implements ITempManager {
+ /** @var string[] Current temporary files and folders, used for cleanup */
+ protected $current = [];
+ /** @var string i.e. /tmp on linux systems */
+ protected $tmpBaseDir;
+ /** @var LoggerInterface */
+ protected $log;
+ /** @var IConfig */
+ protected $config;
+ /** @var IniGetWrapper */
+ protected $iniGetWrapper;
+
+ /** Prefix */
+ public const TMP_PREFIX = 'oc_tmp_';
+
+ public function __construct(LoggerInterface $logger, IConfig $config, IniGetWrapper $iniGetWrapper) {
+ $this->log = $logger;
+ $this->config = $config;
+ $this->iniGetWrapper = $iniGetWrapper;
+ $this->tmpBaseDir = $this->getTempBaseDir();
+ }
+
+ private function generateTemporaryPath(string $postFix): string {
+ $secureRandom = \OCP\Server::get(ISecureRandom::class);
+ $absolutePath = $this->tmpBaseDir . '/' . self::TMP_PREFIX . $secureRandom->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
+
+ if ($postFix !== '') {
+ $postFix = '.' . ltrim($postFix, '.');
+ $postFix = str_replace(['\\', '/'], '', $postFix);
+ }
+
+ return $absolutePath . $postFix;
+ }
+
+ public function getTemporaryFile($postFix = ''): string|false {
+ $path = $this->generateTemporaryPath($postFix);
+
+ $old_umask = umask(0077);
+ $fp = fopen($path, 'x');
+ umask($old_umask);
+ if ($fp === false) {
+ $this->log->warning(
+ 'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions',
+ [
+ 'dir' => $this->tmpBaseDir,
+ ]
+ );
+ return false;
+ }
+
+ fclose($fp);
+ $this->current[] = $path;
+ return $path;
+ }
+
+ public function getTemporaryFolder($postFix = ''): string|false {
+ $path = $this->generateTemporaryPath($postFix) . '/';
+
+ if (mkdir($path, 0700) === false) {
+ $this->log->warning(
+ 'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions',
+ [
+ 'dir' => $this->tmpBaseDir,
+ ]
+ );
+ return false;
+ }
+
+ $this->current[] = $path;
+ return $path;
+ }
+
+ /**
+ * Remove the temporary files and folders generated during this request
+ */
+ public function clean() {
+ $this->cleanFiles($this->current);
+ }
+
+ /**
+ * @param string[] $files
+ */
+ protected function cleanFiles($files) {
+ foreach ($files as $file) {
+ if (file_exists($file)) {
+ try {
+ Files::rmdirr($file);
+ } catch (\UnexpectedValueException $ex) {
+ $this->log->warning(
+ 'Error deleting temporary file/folder: {file} - Reason: {error}',
+ [
+ 'file' => $file,
+ 'error' => $ex->getMessage(),
+ ]
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove old temporary files and folders that were failed to be cleaned
+ */
+ public function cleanOld() {
+ $this->cleanFiles($this->getOldFiles());
+ }
+
+ /**
+ * Get all temporary files and folders generated by oc older than an hour
+ *
+ * @return string[]
+ */
+ protected function getOldFiles() {
+ $cutOfTime = time() - 3600;
+ $files = [];
+ $dh = opendir($this->tmpBaseDir);
+ if ($dh) {
+ while (($file = readdir($dh)) !== false) {
+ if (substr($file, 0, 7) === self::TMP_PREFIX) {
+ $path = $this->tmpBaseDir . '/' . $file;
+ $mtime = filemtime($path);
+ if ($mtime < $cutOfTime) {
+ $files[] = $path;
+ }
+ }
+ }
+ }
+ return $files;
+ }
+
+ /**
+ * Get the temporary base directory configured on the server
+ *
+ * @return string Path to the temporary directory or null
+ * @throws \UnexpectedValueException
+ */
+ public function getTempBaseDir() {
+ if ($this->tmpBaseDir) {
+ return $this->tmpBaseDir;
+ }
+
+ $directories = [];
+ if ($temp = $this->config->getSystemValue('tempdirectory', null)) {
+ $directories[] = $temp;
+ }
+ if ($temp = $this->iniGetWrapper->get('upload_tmp_dir')) {
+ $directories[] = $temp;
+ }
+ if ($temp = getenv('TMP')) {
+ $directories[] = $temp;
+ }
+ if ($temp = getenv('TEMP')) {
+ $directories[] = $temp;
+ }
+ if ($temp = getenv('TMPDIR')) {
+ $directories[] = $temp;
+ }
+ if ($temp = sys_get_temp_dir()) {
+ $directories[] = $temp;
+ }
+
+ foreach ($directories as $dir) {
+ if ($this->checkTemporaryDirectory($dir)) {
+ return $dir;
+ }
+ }
+
+ $temp = tempnam(__DIR__, '');
+ if (file_exists($temp)) {
+ unlink($temp);
+ return dirname($temp);
+ }
+ throw new \UnexpectedValueException('Unable to detect system temporary directory');
+ }
+
+ /**
+ * Check if a temporary directory is ready for use
+ *
+ * @param mixed $directory
+ * @return bool
+ */
+ private function checkTemporaryDirectory($directory) {
+ // suppress any possible errors caused by is_writable
+ // checks missing or invalid path or characters, wrong permissions etc
+ try {
+ if (is_writable($directory)) {
+ return true;
+ }
+ } catch (\Exception $e) {
+ }
+ $this->log->warning('Temporary directory {dir} is not present or writable',
+ ['dir' => $directory]
+ );
+ return false;
+ }
+
+ /**
+ * Override the temporary base directory
+ *
+ * @param string $directory
+ */
+ public function overrideTempBaseDir($directory) {
+ $this->tmpBaseDir = $directory;
+ }
+}