1<?php
2/**
3 *
4 * This file is part of the phpBB Forum Software package.
5 *
6 * @copyright (c) phpBB Limited <https://www.phpbb.com>
7 * @license GNU General Public License, version 2 (GPL-2.0)
8 *
9 * For full copyright and license information, please see
10 * the docs/CREDITS.txt file.
11 *
12 */
13
14namespace phpbb\install\helper\file_updater;
15
16use phpbb\filesystem\exception\filesystem_exception;
17use phpbb\filesystem\filesystem;
18use phpbb\install\exception\file_updater_failure_exception;
19
20/**
21 * File updater for direct filesystem access
22 */
23class file_updater implements file_updater_interface
24{
25	/**
26	 * @var filesystem
27	 */
28	protected $filesystem;
29
30	/**
31	 * @var string
32	 */
33	protected $phpbb_root_path;
34
35	/**
36	 * Constructor
37	 *
38	 * @param filesystem	$filesystem
39	 * @param string		$phpbb_root_path
40	 */
41	public function __construct(filesystem $filesystem, $phpbb_root_path)
42	{
43		$this->filesystem		= $filesystem;
44		$this->phpbb_root_path	= $phpbb_root_path;
45	}
46
47	/**
48	 * {@inheritdoc}
49	 *
50	 * @throws file_updater_failure_exception	When the file is not writable
51	 * @throws filesystem_exception				When the filesystem class fails
52	 */
53	public function delete_file($path_to_file)
54	{
55		$this->filesystem->remove($this->phpbb_root_path . $path_to_file);
56	}
57
58	/**
59	 * {@inheritdoc}
60	 *
61	 * @throws file_updater_failure_exception	When the file is not writable
62	 * @throws filesystem_exception				When the filesystem class fails
63	 */
64	public function create_new_file($path_to_file_to_create, $source, $create_from_content = false)
65	{
66		$path_to_file_to_create = $this->phpbb_root_path . $path_to_file_to_create;
67
68		$dir = dirname($path_to_file_to_create);
69		if (!$this->filesystem->exists($dir))
70		{
71			$this->make_dir($dir);
72		}
73
74		$original_dir_perms = false;
75
76		if (!$this->filesystem->is_writable($dir))
77		{
78			// Extract last 9 bits we actually need
79			$original_dir_perms = @fileperms($dir) & 511;
80			$this->filesystem->phpbb_chmod($dir, filesystem::CHMOD_ALL);
81		}
82
83		if (!$create_from_content)
84		{
85			try
86			{
87				$this->filesystem->copy($source, $path_to_file_to_create);
88			}
89			catch (filesystem_exception $e)
90			{
91				$this->write_file($path_to_file_to_create, $source, $create_from_content);
92			}
93		}
94		else
95		{
96			$this->write_file($path_to_file_to_create, $source, $create_from_content);
97		}
98
99		if ($original_dir_perms !== false)
100		{
101			$this->filesystem->phpbb_chmod($dir, $original_dir_perms);
102		}
103	}
104
105	/**
106	 * {@inheritdoc}
107	 *
108	 * @throws file_updater_failure_exception	When the file is not writable
109	 * @throws filesystem_exception				When the filesystem class fails
110	 */
111	public function update_file($path_to_file_to_update, $source, $create_from_content = false)
112	{
113		$path_to_file_to_update = $this->phpbb_root_path . $path_to_file_to_update;
114		$original_file_perms = false;
115
116		// Maybe necessary for binary files
117		$dir = dirname($path_to_file_to_update);
118		if (!$this->filesystem->exists($dir))
119		{
120			$this->make_dir($dir);
121		}
122
123		if (!$this->filesystem->is_writable($path_to_file_to_update))
124		{
125			// Extract last 9 bits we actually need
126			$original_file_perms = @fileperms($path_to_file_to_update) & 511;
127			$this->filesystem->phpbb_chmod($path_to_file_to_update, filesystem::CHMOD_WRITE);
128		}
129
130		if (!$create_from_content)
131		{
132			try
133			{
134				$this->filesystem->copy($source, $path_to_file_to_update, true);
135			}
136			catch (filesystem_exception $e)
137			{
138				$this->write_file($path_to_file_to_update, $source, $create_from_content);
139			}
140		}
141		else
142		{
143			$this->write_file($path_to_file_to_update, $source, $create_from_content);
144		}
145
146		if ($original_file_perms !== false)
147		{
148			$this->filesystem->phpbb_chmod($path_to_file_to_update, $original_file_perms);
149		}
150	}
151
152	/**
153	 * Creates directory structure
154	 *
155	 * @param string	$path	Path to the directory where the file should be placed (and non-existent)
156	 */
157	private function make_dir($path)
158	{
159		if (is_dir($path))
160		{
161			return;
162		}
163
164		$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
165		$this->filesystem->mkdir($path, 493); // 493 === 0755
166	}
167
168	/**
169	 * Fallback function for file writing
170	 *
171	 * @param string		$path_to_file			Path to the file's location
172	 * @param string		$source					Path to file to copy or string with the new file's content
173	 * @param bool|false	$create_from_content	Whether or not to use $source as the content, false by default
174	 *
175	 * @throws file_updater_failure_exception	When the file is not writable
176	 */
177	private function write_file($path_to_file, $source, $create_from_content = false)
178	{
179		if (!$create_from_content)
180		{
181			$source = @file_get_contents($source);
182		}
183
184		$file_pointer = @fopen($path_to_file, 'w');
185
186		if (!is_resource($file_pointer))
187		{
188			throw new file_updater_failure_exception();
189		}
190
191		@fwrite($file_pointer, $source);
192		@fclose($file_pointer);
193	}
194
195	/**
196	 * {@inheritdoc}
197	 */
198	public function get_method_name()
199	{
200		return 'direct_file';
201	}
202}
203