156 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			156 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
|   | <?php | ||
|  | // This file is part of GNU social - https://www.gnu.org/software/social
 | ||
|  | //
 | ||
|  | // GNU social is free software: you can redistribute it and/or modify
 | ||
|  | // it under the terms of the GNU Affero General Public License as published by
 | ||
|  | // the Free Software Foundation, either version 3 of the License, or
 | ||
|  | // (at your option) any later version.
 | ||
|  | //
 | ||
|  | // GNU social is distributed in the hope that it will be useful,
 | ||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | ||
|  | // GNU Affero General Public License for more details.
 | ||
|  | //
 | ||
|  | // You should have received a copy of the GNU Affero General Public License
 | ||
|  | // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
 | ||
|  | 
 | ||
|  | /** | ||
|  |  * @package   GNUsocial | ||
|  |  * @author    Alexei Sorokin <sor.alexei@meowr.ru> | ||
|  |  * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org | ||
|  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | ||
|  |  */ | ||
|  | 
 | ||
|  | defined('GNUSOCIAL') || die(); | ||
|  | 
 | ||
|  | /** | ||
|  |  * Exception wrapper for TemporaryFile errors | ||
|  |  * | ||
|  |  * @package   GNUsocial | ||
|  |  * @author    Alexei Sorokin <sor.alexei@meowr.ru> | ||
|  |  * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org | ||
|  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | ||
|  |  */ | ||
|  | class TemporaryFileException extends Exception | ||
|  | { | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Class oriented at providing automatic temporary file handling. | ||
|  |  * | ||
|  |  * @package   GNUsocial | ||
|  |  * @author    Alexei Sorokin <sor.alexei@meowr.ru> | ||
|  |  * @copyright 2020 Free Software Foundation, Inc http://www.fsf.org | ||
|  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | ||
|  |  */ | ||
|  | class TemporaryFile extends SplFileInfo | ||
|  | { | ||
|  |     protected $resource = null; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * @param string|null $prefix The file name will begin with that prefix | ||
|  |      *                            ("php" by default) | ||
|  |      * @param string|null $mode File open mode ("w+b" by default) | ||
|  |      */ | ||
|  |     public function __construct( | ||
|  |         ?string $prefix = null, | ||
|  |         ?string $mode   = null | ||
|  |     ) { | ||
|  |         $filename = tempnam(sys_get_temp_dir(), $prefix ?? 'gs-php'); | ||
|  | 
 | ||
|  |         if ($filename === false) { | ||
|  |             throw new TemporaryFileException('Could not create file: ' . $filename); | ||
|  |         } | ||
|  | 
 | ||
|  |         parent::__construct($filename); | ||
|  | 
 | ||
|  |         if (($this->resource = fopen($filename, $mode ?? 'w+b')) === false) { | ||
|  |             $this->cleanup(); | ||
|  |             throw new TemporaryFileException('Could not open file: ' . $filename); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     public function __destruct() | ||
|  |     { | ||
|  |         $this->close(); | ||
|  |         $this->cleanup(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Closes the file descriptor if opened. | ||
|  |      * | ||
|  |      * @return bool Whether successful | ||
|  |      */ | ||
|  |     protected function close(): bool | ||
|  |     { | ||
|  |         $ret = true; | ||
|  |         if (!is_null($this->resource)) { | ||
|  |             $ret = fclose($this->resource); | ||
|  |         } | ||
|  |         if ($ret) { | ||
|  |             $this->resource = null; | ||
|  |         } | ||
|  |         return $ret; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Closes the file descriptor and removes the temporary file. | ||
|  |      * | ||
|  |      * @return void | ||
|  |      */ | ||
|  |     protected function cleanup(): void | ||
|  |     { | ||
|  |         $path = $this->getRealPath(); | ||
|  |         $this->close(); | ||
|  |         if (file_exists($path)) { | ||
|  |             unlink($path); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Get the file resource. | ||
|  |      * | ||
|  |      * @return resource | ||
|  |      */ | ||
|  |     public function getResource() | ||
|  |     { | ||
|  |         return $this->resource; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Release the hold on the temporary file and move it to the desired | ||
|  |      * location, setting file permissions in the process. | ||
|  |      * | ||
|  |      * @param string File destination | ||
|  |      * @param int    New file permissions (in octal mode) | ||
|  |      * @return void | ||
|  |      * @throws TemporaryFileException | ||
|  |      */ | ||
|  |     public function commit(string $destpath, int $umode = 0644): void | ||
|  |     { | ||
|  |         $temppath = $this->getRealPath(); | ||
|  | 
 | ||
|  |         // Might be attempted, and won't end well
 | ||
|  |         if ($destpath === $temppath) { | ||
|  |             throw new TemporaryFileException('Cannot use self as destination'); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Memorise if the file was there and see if there is access
 | ||
|  |         $exists = file_exists($destpath); | ||
|  |         if (!touch($destpath)) { | ||
|  |             throw new TemporaryFileException( | ||
|  |                 'Insufficient permissions for destination: "' . $destpath . '"' | ||
|  |             ); | ||
|  |         } elseif (!$exists) { | ||
|  |             // If the file wasn't there, clean it up in case of a later failure
 | ||
|  |             unlink($destpath); | ||
|  |         } | ||
|  |         if (!$this->close()) { | ||
|  |             throw new TemporaryFileException('Could not close the resource'); | ||
|  |         } | ||
|  | 
 | ||
|  |         rename($temppath, $destpath); | ||
|  |         chmod($destpath, $umode); | ||
|  |     } | ||
|  | } |