Upgraded drupal core with security updates
[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    * @param bool $is_backwards_recursive
142    *   Internal use only.
143    *
144    * @return bool
145    *   TRUE if the directory exists or has been created, FALSE otherwise.
146    */
147   protected function createDirectory($directory, $mode = 0777, $is_backwards_recursive = FALSE) {
148     // If the directory exists already, there's nothing to do.
149     if (is_dir($directory)) {
150       return TRUE;
151     }
152     // Otherwise, try to create the directory and ensure to set its permissions,
153     // because mkdir() obeys the umask of the current process.
154     if (is_dir($parent = dirname($directory))) {
155       // If the parent directory exists, then the backwards recursion must end,
156       // regardless of whether the subdirectory could be created.
157       if ($status = mkdir($directory)) {
158         // Only try to chmod() if the subdirectory could be created.
159         $status = chmod($directory, $mode);
160       }
161       return $is_backwards_recursive ? TRUE : $status;
162     }
163     // If the parent directory and the requested directory does not exist and
164     // could not be created above, walk the requested directory path back up
165     // until an existing directory is hit, and from there, recursively create
166     // the sub-directories. Only if that recursion succeeds, create the final,
167     // originally requested subdirectory.
168     return $this->createDirectory($parent, $mode, TRUE) && mkdir($directory) && chmod($directory, $mode);
169   }
170
171   /**
172    * {@inheritdoc}
173    */
174   public function delete($name) {
175     $path = $this->getFullPath($name);
176     if (file_exists($path)) {
177       return $this->unlink($path);
178     }
179     return FALSE;
180   }
181
182   /**
183    * {@inheritdoc}
184    */
185   public function getFullPath($name) {
186     return $this->directory . '/' . $name;
187   }
188
189   /**
190    * {@inheritdoc}
191    */
192   public function writeable() {
193     return TRUE;
194   }
195
196   /**
197    * {@inheritdoc}
198    */
199   public function deleteAll() {
200     return $this->unlink($this->directory);
201   }
202
203   /**
204    * Deletes files and/or directories in the specified path.
205    *
206    * If the specified path is a directory the method will
207    * call itself recursively to process the contents. Once the contents have
208    * been removed the directory will also be removed.
209    *
210    * @param string $path
211    *   A string containing either a file or directory path.
212    *
213    * @return bool
214    *   TRUE for success or if path does not exist, FALSE in the event of an
215    *   error.
216    */
217   protected function unlink($path) {
218     if (file_exists($path)) {
219       if (is_dir($path)) {
220         // Ensure the folder is writable.
221         @chmod($path, 0777);
222         foreach (new \DirectoryIterator($path) as $fileinfo) {
223           if (!$fileinfo->isDot()) {
224             $this->unlink($fileinfo->getPathName());
225           }
226         }
227         return @rmdir($path);
228       }
229       // Windows needs the file to be writable.
230       @chmod($path, 0700);
231       return @unlink($path);
232     }
233     // If there's nothing to delete return TRUE anyway.
234     return TRUE;
235   }
236
237   /**
238    * {@inheritdoc}
239    */
240   public function listAll() {
241     $names = [];
242     if (file_exists($this->directory)) {
243       foreach (new \DirectoryIterator($this->directory) as $fileinfo) {
244         if (!$fileinfo->isDot()) {
245           $name = $fileinfo->getFilename();
246           if ($name != '.htaccess') {
247             $names[] = $name;
248           }
249         }
250       }
251     }
252     return $names;
253   }
254
255   /**
256    * {@inheritdoc}
257    */
258   public function garbageCollection() {
259   }
260
261 }