]> git.mxchange.org Git - friendica.git/blob - src/Model/Storage/Filesystem.php
- Fixing SystemResource
[friendica.git] / src / Model / Storage / Filesystem.php
1 <?php
2 /**
3  * @file src/Model/Storage/Filesystem.php
4  * @brief Storage backend system
5  */
6
7 namespace Friendica\Model\Storage;
8
9 use Friendica\Core\Config\IConfiguration;
10 use Friendica\Core\L10n\L10n;
11 use Friendica\Util\Strings;
12 use Psr\Log\LoggerInterface;
13
14 /**
15  * @brief Filesystem based storage backend
16  *
17  * This class manage data on filesystem.
18  * Base folder for storage is set in storage.filesystem_path.
19  * Best would be for storage folder to be outside webserver folder, we are using a
20  * folder relative to code tree root as default to ease things for users in shared hostings.
21  * Each new resource gets a value as reference and is saved in a
22  * folder tree stucture created from that value.
23  */
24 class Filesystem extends AbstractStorage
25 {
26         const NAME = 'Filesystem';
27
28         // Default base folder
29         const DEFAULT_BASE_FOLDER = 'storage';
30
31         /** @var IConfiguration */
32         private $config;
33
34         /** @var string */
35         private $basePath;
36
37         /**
38          * Filesystem constructor.
39          *
40          * @param IConfiguration  $config
41          * @param LoggerInterface $logger
42          * @param L10n            $l10n
43          */
44         public function __construct(IConfiguration $config, LoggerInterface $logger, L10n $l10n)
45         {
46                 parent::__construct($l10n, $logger);
47
48                 $this->config = $config;
49
50                 $path           = $this->config->get('storage', 'filesystem_path', self::DEFAULT_BASE_FOLDER);
51                 $this->basePath = rtrim($path, '/');
52         }
53
54         /**
55          * @brief Split data ref and return file path
56          *
57          * @param string $reference Data reference
58          *
59          * @return string
60          */
61         private function pathForRef(string $reference)
62         {
63                 $fold1 = substr($reference, 0, 2);
64                 $fold2 = substr($reference, 2, 2);
65                 $file  = substr($reference, 4);
66
67                 return implode('/', [$this->basePath, $fold1, $fold2, $file]);
68         }
69
70
71         /**
72          * @brief Create dirctory tree to store file, with .htaccess and index.html files
73          *
74          * @param string $file Path and filename
75          *
76          * @throws StorageException
77          */
78         private function createFoldersForFile(string $file)
79         {
80                 $path = dirname($file);
81
82                 if (!is_dir($path)) {
83                         if (!mkdir($path, 0770, true)) {
84                                 $this->logger->warning('Failed to create dir.', ['path' => $path]);
85                                 throw new StorageException($this->l10n->t('Filesystem storage failed to create "%s". Check you write permissions.', $path));
86                         }
87                 }
88
89                 while ($path !== $this->basePath) {
90                         if (!is_file($path . '/index.html')) {
91                                 file_put_contents($path . '/index.html', '');
92                         }
93                         chmod($path . '/index.html', 0660);
94                         chmod($path, 0770);
95                         $path = dirname($path);
96                 }
97                 if (!is_file($path . '/index.html')) {
98                         file_put_contents($path . '/index.html', '');
99                         chmod($path . '/index.html', 0660);
100                 }
101         }
102
103         /**
104          * @inheritDoc
105          */
106         public function get(string $reference)
107         {
108                 $file = $this->pathForRef($reference);
109                 if (!is_file($file)) {
110                         return '';
111                 }
112
113                 return file_get_contents($file);
114         }
115
116         /**
117          * @inheritDoc
118          */
119         public function put(string $data, string $reference = '')
120         {
121                 if ($reference === '') {
122                         $reference = Strings::getRandomHex();
123                 }
124                 $file = $this->pathForRef($reference);
125
126                 $this->createFoldersForFile($file);
127
128                 if ((file_exists($file) && !is_writable($file)) || !file_put_contents($file, $data)) {
129                         $this->logger->warning('Failed to write data.', ['file' => $file]);
130                         throw new StorageException($this->l10n->t('Filesystem storage failed to save data to "%s". Check your write permissions', $file));
131                 }
132
133                 chmod($file, 0660);
134                 return $reference;
135         }
136
137         /**
138          * @inheritDoc
139          */
140         public function delete(string $reference)
141         {
142                 $file = $this->pathForRef($reference);
143                 // return true if file doesn't exists. we want to delete it: success with zero work!
144                 if (!is_file($file)) {
145                         return true;
146                 }
147                 return unlink($file);
148         }
149
150         /**
151          * @inheritDoc
152          */
153         public function getOptions()
154         {
155                 return [
156                         'storagepath' => [
157                                 'input',
158                                 $this->l10n->t('Storage base path'),
159                                 $this->basePath,
160                                 $this->l10n->t('Folder where uploaded files are saved. For maximum security, This should be a path outside web server folder tree')
161                         ]
162                 ];
163         }
164
165         /**
166          * @inheritDoc
167          */
168         public function saveOptions(array $data)
169         {
170                 $storagePath = $data['storagepath'] ?? '';
171                 if ($storagePath === '' || !is_dir($storagePath)) {
172                         return [
173                                 'storagepath' => $this->l10n->t('Enter a valid existing folder')
174                         ];
175                 };
176                 $this->config->set('storage', 'filesystem_path', $storagePath);
177                 $this->basePath = $storagePath;
178                 return [];
179         }
180
181         /**
182          * @inheritDoc
183          */
184         public static function getName()
185         {
186                 return self::NAME;
187         }
188 }