Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Component / PhpStorage / FileStorage.php
1 <?php
2
3 namespace Drupal\Component\PhpStorage;
4
5 /**
6  * Stores the code as regular PHP files.
7  */
8 class FileStorage implements PhpStorageInterface {
9
10   /**
11    * The directory where the files should be stored.
12    *
13    * @var string
14    */
15   protected $directory;
16
17   /**
18    * Constructs this FileStorage object.
19    *
20    * @param array $configuration
21    *   An associative array, containing at least these two keys:
22    *   - directory: The directory where the files should be stored.
23    *   - bin: The storage bin. Multiple storage objects can be instantiated with
24    *     the same configuration, but for different bins..
25    */
26   public function __construct(array $configuration) {
27     $this->directory = $configuration['directory'] . '/' . $configuration['bin'];
28   }
29
30   /**
31    * {@inheritdoc}
32    */
33   public function exists($name) {
34     return file_exists($this->getFullPath($name));
35   }
36
37   /**
38    * {@inheritdoc}
39    */
40   public function load($name) {
41     // The FALSE returned on failure is enough for the caller to handle this,
42     // we do not want a warning too.
43     return (@include_once $this->getFullPath($name)) !== FALSE;
44   }
45
46   /**
47    * {@inheritdoc}
48    */
49   public function save($name, $code) {
50     $path = $this->getFullPath($name);
51     $directory = dirname($path);
52     $this->ensureDirectory($directory);
53     return (bool) file_put_contents($path, $code);
54   }
55
56   /**
57    * Returns the standard .htaccess lines that Drupal writes to file directories.
58    *
59    * @param bool $private
60    *   (optional) Set to FALSE to return the .htaccess lines for an open and
61    *   public directory. The default is TRUE, which returns the .htaccess lines
62    *   for a private and protected directory.
63    *
64    * @return string
65    *   The desired contents of the .htaccess file.
66    *
67    * @see file_create_htaccess()
68    */
69   public static function htaccessLines($private = TRUE) {
70     $lines = <<<EOF
71 # Turn off all options we don't need.
72 Options -Indexes -ExecCGI -Includes -MultiViews
73
74 # Set the catch-all handler to prevent scripts from being executed.
75 SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006
76 <Files *>
77   # Override the handler again if we're run later in the evaluation list.
78   SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003
79 </Files>
80
81 # If we know how to do it safely, disable the PHP engine entirely.
82 <IfModule mod_php5.c>
83   php_flag engine off
84 </IfModule>
85 EOF;
86
87     if ($private) {
88       $lines = <<<EOF
89 # Deny all requests from Apache 2.4+.
90 <IfModule mod_authz_core.c>
91   Require all denied
92 </IfModule>
93
94 # Deny all requests from Apache 2.0-2.2.
95 <IfModule !mod_authz_core.c>
96   Deny from all
97 </IfModule>
98 $lines
99 EOF;
100     }
101
102     return $lines;
103   }
104
105   /**
106    * Ensures the directory exists, has the right permissions, and a .htaccess.
107    *
108    * For compatibility with open_basedir, the requested directory is created
109    * using a recursion logic that is based on the relative directory path/tree:
110    * It works from the end of the path recursively back towards the root
111    * directory, until an existing parent directory is found. From there, the
112    * subdirectories are created.
113    *
114    * @param string $directory
115    *   The directory path.
116    * @param int $mode
117    *   The mode, permissions, the directory should have.
118    */
119   protected function ensureDirectory($directory, $mode = 0777) {
120     if ($this->createDirectory($directory, $mode)) {
121       $htaccess_path = $directory . '/.htaccess';
122       if (!file_exists($htaccess_path) && file_put_contents($htaccess_path, static::htaccessLines())) {
123         @chmod($htaccess_path, 0444);
124       }
125     }
126   }
127
128   /**
129    * Ensures the requested directory exists and has the right permissions.
130    *
131    * For compatibility with open_basedir, the requested directory is created
132    * using a recursion logic that is based on the relative directory path/tree:
133    * It works from the end of the path recursively back towards the root
134    * directory, until an existing parent directory is found. From there, the
135    * subdirectories are created.
136    *
137    * @param string $directory
138    *   The directory path.
139    * @param int $mode
140    *   The mode, permissions, the directory should have.
141    *
142    * @return bool
143    *   TRUE if the directory exists or has been created, FALSE otherwise.
144    */
145   protected function createDirectory($directory, $mode = 0777) {
146     // If the directory exists already, there's nothing to do.
147     if (is_dir($directory)) {
148       return TRUE;
149     }
150
151     // If the parent directory doesn't exist, try to create it.
152     $parent_exists = is_dir($parent = dirname($directory));
153     if (!$parent_exists) {
154       $parent_exists = $this->createDirectory($parent, $mode);
155     }
156
157     // If parent exists, try to create the directory and ensure to set its
158     // permissions, because mkdir() obeys the umask of the current process.
159     if ($parent_exists) {
160       // We hide warnings and ignore the return because there may have been a
161       // race getting here and the directory could already exist.
162       @mkdir($directory);
163       // Only try to chmod() if the subdirectory could be created.
164       if (is_dir($directory)) {
165         // Avoid writing permissions if possible.
166         if (fileperms($directory) !== $mode) {
167           return chmod($directory, $mode);
168         }
169         return TRUE;
170       }
171       else {
172         // Something failed and the directory doesn't exist.
173         trigger_error('mkdir(): Permission Denied', E_USER_WARNING);
174       }
175     }
176     return FALSE;
177   }
178
179   /**
180    * {@inheritdoc}
181    */
182   public function delete($name) {
183     $path = $this->getFullPath($name);
184     if (file_exists($path)) {
185       return $this->unlink($path);
186     }
187     return FALSE;
188   }
189
190   /**
191    * {@inheritdoc}
192    */
193   public function getFullPath($name) {
194     return $this->directory . '/' . $name;
195   }
196
197   /**
198    * {@inheritdoc}
199    */
200   public function writeable() {
201     return TRUE;
202   }
203
204   /**
205    * {@inheritdoc}
206    */
207   public function deleteAll() {
208     return $this->unlink($this->directory);
209   }
210
211   /**
212    * Deletes files and/or directories in the specified path.
213    *
214    * If the specified path is a directory the method will
215    * call itself recursively to process the contents. Once the contents have
216    * been removed the directory will also be removed.
217    *
218    * @param string $path
219    *   A string containing either a file or directory path.
220    *
221    * @return bool
222    *   TRUE for success or if path does not exist, FALSE in the event of an
223    *   error.
224    */
225   protected function unlink($path) {
226     if (file_exists($path)) {
227       if (is_dir($path)) {
228         // Ensure the folder is writable.
229         @chmod($path, 0777);
230         foreach (new \DirectoryIterator($path) as $fileinfo) {
231           if (!$fileinfo->isDot()) {
232             $this->unlink($fileinfo->getPathName());
233           }
234         }
235         return @rmdir($path);
236       }
237       // Windows needs the file to be writable.
238       @chmod($path, 0700);
239       return @unlink($path);
240     }
241     // If there's nothing to delete return TRUE anyway.
242     return TRUE;
243   }
244
245   /**
246    * {@inheritdoc}
247    */
248   public function listAll() {
249     $names = [];
250     if (file_exists($this->directory)) {
251       foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
252         if (!$fileinfo->isDot()) {
253           $name = $fileinfo->getFilename();
254           if ($name != '.htaccess') {
255             $names[] = $name;
256           }
257         }
258       }
259     }
260     return $names;
261   }
262
263   /**
264    * {@inheritdoc}
265    */
266   public function garbageCollection() {
267   }
268
269 }