xref: /webtrees/app/Services/HousekeepingService.php (revision e99ac601cee7ced57ac8545ceba47ece1b15d883)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2022 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Services;
21
22use Fisharebest\Webtrees\Registry;
23use Illuminate\Database\Capsule\Manager as DB;
24use League\Flysystem\FilesystemException;
25use League\Flysystem\FilesystemOperator;
26use League\Flysystem\FilesystemReader;
27use League\Flysystem\UnableToDeleteDirectory;
28use League\Flysystem\UnableToDeleteFile;
29
30use function date;
31use function time;
32
33/**
34 * Clean up old data, files and folders.
35 */
36class HousekeepingService
37{
38    // This is a list of old files and directories, from earlier versions of webtrees.
39    // git diff 1.7.9..master --name-status | grep ^D
40    private const OLD_PATHS = [
41        // Removed in 1.0.3
42        'themechange.php',
43        // Removed in 1.1.0
44        'addremotelink.php',
45        'addsearchlink.php',
46        'client.php',
47        'dir_editor.php',
48        'editconfig_gedcom.php',
49        'editgedcoms.php',
50        'edit_merge.php',
51        'edit_news.php',
52        'genservice.php',
53        'logs.php',
54        'manageservers.php',
55        'media.php',
56        'module_admin.php',
57        //'modules', // Do not delete - users may have stored custom modules/data here
58        'opensearch.php',
59        'PEAR.php',
60        'pgv_to_wt.php',
61        'places',
62        //'robots.txt', // Do not delete this - it may contain user data
63        'serviceClientTest.php',
64        'siteconfig.php',
65        'SOAP',
66        'uploadmedia.php',
67        'useradmin.php',
68        'webservice',
69        'wtinfo.php',
70        // Removed in 1.1.2
71        'treenav.php',
72        // Removed in 1.2.3
73        'modules_v2',
74        // Removed in 1.2.4
75        'search_engine.php',
76        // Removed in 1.2.5
77        'sidebar.php',
78        // Removed in 1.2.6
79        // Removed in 1.2.7
80        'login_register.php',
81        // Removed in 1.3.0
82        'admin_site_ipaddress.php',
83        'downloadgedcom.php',
84        'export_gedcom.php',
85        'gedcheck.php',
86        'images',
87        // Removed in 1.3.1
88        'imageflush.php',
89        '/lightbox/js/tip_balloon_RTL.js',
90        // Removed in 1.4.0
91        'imageview.php',
92        'media/MediaInfo.txt',
93        'media/thumbs/ThumbsInfo.txt',
94        // Removed in 1.5.3
95        'readme.html',
96        // Removed in 1.6.0
97        'downloadbackup.php',
98        'site-php-version.php',
99        // Removed in 1.7.0
100        'admin_site_other.php',
101        'js',
102        'library',
103        'save.php',
104        // Removed in 1.7.2
105        'assets/js-1.7.0',
106        // Removed in 1.7.4
107        'assets/js-1.7.2',
108        // Removed in 1.7.7
109        'assets/js-1.7.4',
110        // Removed in 2.0.0
111        'action.php',
112        'addmedia.php',
113        'addmin.php',
114        'admin_media.php',
115        'admin_media_upload.php',
116        'admin_module_blocks.php',
117        'admin_module_charts.php',
118        'admin_module_menus.php',
119        'admin_module_reports.php',
120        'admin_module_sidebar.php',
121        'admin_module_tabs.php',
122        'admin_modules.php',
123        'admin_pgv_to_wt.php',
124        'admin_site_access.php',
125        'admin_site_change.php',
126        'admin_site_clean.php',
127        'admin_site_config.php',
128        'admin_site_info.php',
129        'admin_site_logs.php',
130        'admin_site_merge.php',
131        'admin_site_readme.php',
132        'admin_site_upgrade.php',
133        'admin_trees_check.php',
134        'admin_trees_config.php',
135        'admin_trees_download.php',
136        'admin_trees_duplicates.php',
137        'admin_trees_export.php',
138        'admin_trees_manage.php',
139        'admin_trees_merge.php',
140        'admin_trees_places.php',
141        'admin_trees_renumber.php',
142        'admin_trees_unconnected.php',
143        'admin_users.php',
144        'admin_users_bulk.php',
145        'ancestry.php',
146        'app/Controller',
147        'app/HitCounter.php',
148        'app/Module/ClippingsCart/ClippingsCartController.php',
149        'app/Module/FamiliesSidebarModule.php',
150        'app/Module/FamilyTreeFavorites',
151        'app/Module/GoogleMaps',
152        'app/Module/IndividualSidebarModule.php',
153        'app/Module/PageMenuModule.php',
154        'app/Query',
155        'app/SpecialChars',
156        'assets/js-1.7.7',
157        'assets/js-1.7.9',
158        'autocomplete.php',
159        'block_edit.php',
160        'branches.php',
161        'calendar.php',
162        'compact.php',
163        'data/html_purifier_cache',
164        'descendancy.php',
165        'editnews.php',
166        'edituser.php',
167        'edit_changes.php',
168        'edit_interface.php',
169        'expand_view.php',
170        'familybook.php',
171        'famlist.php',
172        'fanchart.php',
173        'find.php',
174        'help_text.php',
175        'hourglass.php',
176        'hourglass_ajax.php',
177        'import.php',
178        'includes',
179        'index_edit.php',
180        'indilist.php',
181        'inverselink.php',
182        'language',
183        'lifespan.php',
184        'login.php',
185        'logout.php',
186        'mediafirewall.php',
187        'medialist.php',
188        'message.php',
189        'module.php',
190        'modules_v3',
191        'notelist.php',
192        'packages',
193        'pedigree.php',
194        'relationship.php',
195        'repolist.php',
196        'reportengine.php',
197        'search.php',
198        'search_advanced.php',
199        'site-offline.php',
200        'site-unavailable.php',
201        'sourcelist.php',
202        'statistics.php',
203        'statisticsplot.php',
204        'themes',
205        'timeline.php',
206        // Removed in 2.0.3
207        'public/css/vendor.css',
208        // Removed in 2.0.4
209        'public/favicon-120.png',
210        'public/favicon-144.png',
211        'public/favicon-57.png',
212        'public/favicon-76.png',
213        'public/favicon-96.png',
214        // Removed in 2.0.7
215        'public/ckeditor-4.11.2-custom',
216        // Removed in 2.0.12
217        'public/ckeditor-4.14.1-custom',
218        // Removed in 2.1.0
219        'modules_v4/example-footer.disable',
220        'modules_v4/example-middleware.disable',
221        'modules_v4/example-report.disable',
222        'modules_v4/example-report.disable',
223        'modules_v4/example-server-configuration.disable',
224        'modules_v4/example-theme.disable',
225        'modules_v4/example.disable',
226        'public/favicon-196.png',
227        'public/site.webmanifest',
228    ];
229
230    /**
231     * Delete files and folders that belonged to an earlier version of webtrees.
232     * Return a list of those that we could not delete.
233     *
234     * @param FilesystemOperator $filesystem
235     *
236     * @return array<string>
237     */
238    public function deleteOldWebtreesFiles(FilesystemOperator $filesystem): array
239    {
240        $paths_to_delete = [];
241
242        foreach (self::OLD_PATHS as $path) {
243            if (!$this->deleteFileOrFolder($filesystem, $path)) {
244                $paths_to_delete[] = $path;
245            }
246        }
247
248        return $paths_to_delete;
249    }
250
251    /**
252     * Delete old cache files.
253     *
254     * @param FilesystemOperator $filesystem
255     * @param string             $path
256     * @param int                $max_age Seconds
257     *
258     * @return void
259     */
260    public function deleteOldFiles(FilesystemOperator $filesystem, string $path, int $max_age): void
261    {
262        $threshold = time() - $max_age;
263
264        $list = $filesystem->listContents($path, FilesystemReader::LIST_DEEP);
265
266        foreach ($list as $metadata) {
267            // The timestamp can be absent or false.
268            $timestamp = $metadata['timestamp'] ?? false;
269
270            if ($timestamp !== false && $timestamp < $threshold) {
271                $this->deleteFileOrFolder($filesystem, $metadata['path']);
272            }
273        }
274    }
275
276    /**
277     * @param int $max_age_in_seconds
278     *
279     * @return void
280     */
281    public function deleteOldLogs(int $max_age_in_seconds): void
282    {
283        DB::table('log')
284            ->whereIn('log_type', ['error', 'media'])
285            ->where('log_time', '<', date('Y-m-d H:i:s', time() - $max_age_in_seconds))
286            ->delete();
287    }
288
289    /**
290     * @param int $max_age_in_seconds
291     *
292     * @return void
293     */
294    public function deleteOldSessions(int $max_age_in_seconds): void
295    {
296        DB::table('session')
297            ->where('session_time', '<', date('Y-m-d H:i:s', time() - $max_age_in_seconds))
298            ->delete();
299    }
300
301    /**
302     * Delete a file or folder, if we can.
303     *
304     * @param FilesystemOperator $filesystem
305     * @param string             $path
306     *
307     * @return bool
308     */
309    private function deleteFileOrFolder(FilesystemOperator $filesystem, string $path): bool
310    {
311        try {
312            $filesystem->delete($path);
313        } catch (FilesystemException | UnableToDeleteFile $ex) {
314            try {
315                $filesystem->deleteDirectory($path);
316            } catch (FilesystemException | UnableToDeleteDirectory $ex) {
317                return false;
318            }
319        }
320
321        return true;
322    }
323}
324