xref: /webtrees/app/Services/HousekeepingService.php (revision f32d77e63d71326973bfe0496adeec694ca90ecf)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 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 Exception;
23use Fisharebest\Webtrees\Carbon;
24use Illuminate\Database\Capsule\Manager as DB;
25use League\Flysystem\Filesystem;
26use League\Flysystem\FilesystemException;
27use League\Flysystem\FilesystemOperator;
28use League\Flysystem\UnableToDeleteDirectory;
29use League\Flysystem\UnableToDeleteFile;
30
31/**
32 * Clean up old data, files and folders.
33 */
34class HousekeepingService
35{
36    // This is a list of old files and directories, from earlier versions of webtrees.
37    // git diff 1.7.9..master --name-status | grep ^D
38    private const OLD_PATHS = [
39        // Removed in 1.0.3
40        'themechange.php',
41        // Removed in 1.1.0
42        'addremotelink.php',
43        'addsearchlink.php',
44        'client.php',
45        'dir_editor.php',
46        'editconfig_gedcom.php',
47        'editgedcoms.php',
48        'edit_merge.php',
49        'edit_news.php',
50        'genservice.php',
51        'logs.php',
52        'manageservers.php',
53        'media.php',
54        'module_admin.php',
55        //'modules', // Do not delete - users may have stored custom modules/data here
56        'opensearch.php',
57        'PEAR.php',
58        'pgv_to_wt.php',
59        'places',
60        //'robots.txt', // Do not delete this - it may contain user data
61        'serviceClientTest.php',
62        'siteconfig.php',
63        'SOAP',
64        'themes/clouds/mozilla.css',
65        'themes/clouds/netscape.css',
66        'themes/colors/mozilla.css',
67        'themes/colors/netscape.css',
68        'themes/fab/mozilla.css',
69        'themes/fab/netscape.css',
70        'themes/minimal/mozilla.css',
71        'themes/minimal/netscape.css',
72        'themes/webtrees/mozilla.css',
73        'themes/webtrees/netscape.css',
74        'themes/webtrees/style_rtl.css',
75        'themes/xenea/mozilla.css',
76        'themes/xenea/netscape.css',
77        'uploadmedia.php',
78        'useradmin.php',
79        'webservice',
80        'wtinfo.php',
81        // Removed in 1.1.2
82        'treenav.php',
83        // Removed in 1.2.0
84        'themes/clouds/jquery',
85        'themes/colors/jquery',
86        'themes/fab/jquery',
87        'themes/minimal/jquery',
88        'themes/webtrees/jquery',
89        'themes/xenea/jquery',
90        // Removed in 1.2.2
91        'themes/clouds/chrome.css',
92        'themes/clouds/opera.css',
93        'themes/clouds/print.css',
94        'themes/clouds/style_rtl.css',
95        'themes/colors/chrome.css',
96        'themes/colors/opera.css',
97        'themes/colors/print.css',
98        'themes/colors/style_rtl.css',
99        'themes/fab/chrome.css',
100        'themes/fab/opera.css',
101        'themes/minimal/chrome.css',
102        'themes/minimal/opera.css',
103        'themes/minimal/print.css',
104        'themes/minimal/style_rtl.css',
105        'themes/xenea/chrome.css',
106        'themes/xenea/opera.css',
107        'themes/xenea/print.css',
108        'themes/xenea/style_rtl.css',
109        // Removed in 1.2.3
110        'modules_v2',
111        // Removed in 1.2.4
112        'search_engine.php',
113        'themes/clouds/modules.css',
114        'themes/colors/modules.css',
115        'themes/fab/modules.css',
116        'themes/minimal/modules.css',
117        'themes/webtrees/modules.css',
118        'themes/xenea/modules.css',
119        // Removed in 1.2.5
120        'sidebar.php',
121        // Removed in 1.2.6
122        // Removed in 1.2.7
123        'login_register.php',
124        // Removed in 1.3.0
125        'admin_site_ipaddress.php',
126        'downloadgedcom.php',
127        'export_gedcom.php',
128        'gedcheck.php',
129        'images',
130        // Removed in 1.3.1
131        'imageflush.php',
132        '/lightbox/js/tip_balloon_RTL.js',
133        // Removed in 1.4.0
134        'imageview.php',
135        'media/MediaInfo.txt',
136        'media/thumbs/ThumbsInfo.txt',
137        'themes/webtrees/chrome.css',
138        // Removed in 1.4.2
139        'themes/clouds/jquery-ui-1.10.0',
140        'themes/colors/jquery-ui-1.10.0',
141        'themes/fab/jquery-ui-1.10.0',
142        'themes/minimal/jquery-ui-1.10.0',
143        'themes/webtrees/jquery-ui-1.10.0',
144        'themes/xenea/jquery-ui-1.10.0',
145        // Removed in 1.5.0
146        'themes/clouds/favicon.png',
147        'themes/clouds/images',
148        'themes/clouds/msie.css',
149        'themes/clouds/style.css',
150        'themes/colors/css',
151        'themes/colors/favicon.png',
152        'themes/colors/images',
153        'themes/colors/ipad.css',
154        'themes/colors/msie.css',
155        'themes/fab/favicon.png',
156        'themes/fab/images',
157        'themes/fab/msie.css',
158        'themes/fab/style.css',
159        'themes/minimal/favicon.png',
160        'themes/minimal/images',
161        'themes/minimal/msie.css',
162        'themes/minimal/style.css',
163        'themes/webtrees/favicon.png',
164        'themes/webtrees/images',
165        'themes/webtrees/msie.css',
166        'themes/webtrees/style.css',
167        'themes/xenea/favicon.png',
168        'themes/xenea/images',
169        'themes/xenea/msie.css',
170        'themes/xenea/style.css',
171        // Removed in 1.5.1
172        'themes/clouds/css-1.5.0',
173        'themes/colors/css-1.5.0',
174        'themes/fab/css-1.5.0',
175        'themes/minimal/css-1.5.0',
176        'themes/webtrees/css-1.5.0',
177        'themes/xenea/css-1.5.0',
178        // Removed in 1.5.2
179        'themes/clouds/css-1.5.1',
180        'themes/colors/css-1.5.1',
181        'themes/fab/css-1.5.1',
182        'themes/minimal/css-1.5.1',
183        'themes/webtrees/css-1.5.1',
184        'themes/xenea/css-1.5.1',
185        // Removed in 1.5.3
186        'readme.html',
187        'themes/clouds/css-1.5.2',
188        'themes/colors/css-1.5.2',
189        'themes/fab/css-1.5.2',
190        'themes/minimal/css-1.5.2',
191        'themes/webtrees/css-1.5.2',
192        'themes/xenea/css-1.5.2',
193        // Removed in 1.6.0
194        'downloadbackup.php',
195        'site-php-version.php',
196        'themes/clouds/css-1.5.3',
197        'themes/colors/css-1.5.3',
198        'themes/fab/css-1.5.3',
199        'themes/minimal/css-1.5.3',
200        'themes/webtrees/css-1.5.3',
201        'themes/xenea/css-1.5.3',
202        // Removed in 1.6.2
203        'themes/clouds/jquery-ui-1.10.3',
204        'themes/colors/css-1.6.0',
205        'themes/colors/jquery-ui-1.10.3',
206        'themes/fab/css-1.6.0',
207        'themes/fab/jquery-ui-1.10.3',
208        'themes/minimal/css-1.6.0',
209        'themes/minimal/jquery-ui-1.10.3',
210        'themes/webtrees/css-1.6.0',
211        'themes/webtrees/jquery-ui-1.10.3',
212        'themes/xenea/css-1.6.0',
213        'themes/xenea/jquery-ui-1.10.3',
214        // Removed in 1.7.0
215        'admin_site_other.php',
216        'js',
217        'library',
218        'save.php',
219        'themes/clouds/css-1.6.2',
220        'themes/clouds/templates',
221        'themes/clouds/header.php',
222        'themes/clouds/footer.php',
223        'themes/colors/css-1.6.2',
224        'themes/colors/templates',
225        'themes/colors/header.php',
226        'themes/colors/footer.php',
227        'themes/fab/css-1.6.2',
228        'themes/fab/templates',
229        'themes/fab/header.php',
230        'themes/fab/footer.php',
231        'themes/minimal/css-1.6.2',
232        'themes/minimal/templates',
233        'themes/minimal/header.php',
234        'themes/minimal/footer.php',
235        'themes/webtrees/css-1.6.2',
236        'themes/webtrees/templates',
237        'themes/webtrees/header.php',
238        'themes/webtrees/footer.php',
239        'themes/xenea/css-1.6.2',
240        'themes/xenea/templates',
241        'themes/xenea/header.php',
242        'themes/xenea/footer.php',
243        // Removed in 1.7.2
244        'assets/js-1.7.0',
245        // Removed in 1.7.4
246        'assets/js-1.7.2',
247        'themes/clouds/css-1.7.0',
248        'themes/colors/css-1.7.0',
249        'themes/fab/css-1.7.0',
250        'themes/minimal/css-1.7.0',
251        'themes/webtrees/css-1.7.0',
252        'themes/xenea/css-1.7.0',
253        // Removed in 1.7.5
254        'themes/clouds/css-1.7.4',
255        'themes/colors/css-1.7.4',
256        'themes/fab/css-1.7.4',
257        'themes/minimal/css-1.7.4',
258        'themes/webtrees/css-1.7.4',
259        'themes/xenea/css-1.7.4',
260        // Removed in 1.7.7
261        'assets/js-1.7.4',
262        // Removed in 1.7.8
263        'themes/clouds/css-1.7.5',
264        'themes/colors/css-1.7.5',
265        'themes/fab/css-1.7.5',
266        'themes/minimal/css-1.7.5',
267        'themes/webtrees/css-1.7.5',
268        'themes/xenea/css-1.7.5',
269        // Removed in 2.0.0
270        'action.php',
271        'addmedia.php',
272        'addmin.php',
273        'admin_media.php',
274        'admin_media_upload.php',
275        'admin_module_blocks.php',
276        'admin_module_charts.php',
277        'admin_module_menus.php',
278        'admin_module_reports.php',
279        'admin_module_sidebar.php',
280        'admin_module_tabs.php',
281        'admin_modules.php',
282        'admin_pgv_to_wt.php',
283        'admin_site_access.php',
284        'admin_site_change.php',
285        'admin_site_clean.php',
286        'admin_site_config.php',
287        'admin_site_info.php',
288        'admin_site_logs.php',
289        'admin_site_merge.php',
290        'admin_site_readme.php',
291        'admin_site_upgrade.php',
292        'admin_trees_check.php',
293        'admin_trees_config.php',
294        'admin_trees_download.php',
295        'admin_trees_duplicates.php',
296        'admin_trees_export.php',
297        'admin_trees_manage.php',
298        'admin_trees_merge.php',
299        'admin_trees_places.php',
300        'admin_trees_renumber.php',
301        'admin_trees_unconnected.php',
302        'admin_users.php',
303        'admin_users_bulk.php',
304        'ancestry.php',
305        'app/Controller',
306        'app/HitCounter.php',
307        'app/Module/ClippingsCart/ClippingsCartController.php',
308        'app/Module/FamiliesSidebarModule.php',
309        'app/Module/FamilyTreeFavorites',
310        'app/Module/GoogleMaps',
311        'app/Module/IndividualSidebarModule.php',
312        'app/Module/PageMenuModule.php',
313        'app/Query',
314        'app/SpecialChars',
315        'assets/js-1.7.7',
316        'assets/js-1.7.9',
317        'autocomplete.php',
318        'block_edit.php',
319        'branches.php',
320        'calendar.php',
321        'compact.php',
322        'data/html_purifier_cache',
323        'descendancy.php',
324        'editnews.php',
325        'edituser.php',
326        'edit_changes.php',
327        'edit_interface.php',
328        'expand_view.php',
329        'familybook.php',
330        'famlist.php',
331        'fanchart.php',
332        'find.php',
333        'help_text.php',
334        'hourglass.php',
335        'hourglass_ajax.php',
336        'import.php',
337        'includes',
338        'index_edit.php',
339        'indilist.php',
340        'inverselink.php',
341        'language',
342        'lifespan.php',
343        'login.php',
344        'logout.php',
345        'mediafirewall.php',
346        'medialist.php',
347        'message.php',
348        'module.php',
349        'modules_v3',
350        'notelist.php',
351        'packages',
352        'pedigree.php',
353        'relationship.php',
354        'repolist.php',
355        'reportengine.php',
356        'search.php',
357        'search_advanced.php',
358        'site-offline.php',
359        'site-unavailable.php',
360        'sourcelist.php',
361        'statistics.php',
362        'statisticsplot.php',
363        'themes/_administration',
364        'themes/_custom',
365        'themes/clouds/css-1.7.8',
366        'themes/clouds/jquery-ui-1.11.2',
367        'themes/colors/css-1.7.8',
368        'themes/colors/jquery-ui-1.11.2',
369        'themes/fab/css-1.7.8',
370        'themes/fab/jquery-ui-1.11.2',
371        'themes/minimal/css-1.7.8',
372        'themes/minimal/jquery-ui-1.11.2',
373        'themes/webtrees/css-1.7.8',
374        'themes/webtrees/jquery-ui-1.11.2',
375        'themes/xenea/css-1.7.8',
376        'themes/xenea/jquery-ui-1.11.2',
377        'timeline.php',
378    ];
379
380    /**
381     * Delete files and folders that belonged to an earlier version of webtrees.
382     * Return a list of those that we could not delete.
383     *
384     * @param FilesystemOperator $filesystem
385     *
386     * @return array<string>
387     */
388    public function deleteOldWebtreesFiles(FilesystemOperator $filesystem): array
389    {
390        $paths_to_delete = [];
391
392        foreach (self::OLD_PATHS as $path) {
393            if (!$this->deleteFileOrFolder($filesystem, $path)) {
394                $paths_to_delete[] = $path;
395            }
396        }
397
398        return $paths_to_delete;
399    }
400
401    /**
402     * Delete old cache files.
403     *
404     * @param FilesystemOperator $filesystem
405     * @param string             $path
406     * @param int                $max_age Seconds
407     *
408     * @return void
409     */
410    public function deleteOldFiles(FilesystemOperator $filesystem, string $path, int $max_age): void
411    {
412        $threshold = Carbon::now()->unix() - $max_age;
413
414        $list = $filesystem->listContents($path, Filesystem::LIST_DEEP);
415
416        foreach ($list as $metadata) {
417            // The timestamp can be absent or false.
418            $timestamp = $metadata['timestamp'] ?? false;
419
420            if ($timestamp !== false && $timestamp < $threshold) {
421                $this->deleteFileOrFolder($filesystem, $metadata['path']);
422            }
423        }
424    }
425
426    /**
427     * @param int $max_age_in_seconds
428     *
429     * @return void
430     */
431    public function deleteOldLogs(int $max_age_in_seconds): void
432    {
433        $timestamp = Carbon::now()->subSeconds($max_age_in_seconds);
434
435        DB::table('log')
436            ->whereIn('log_type', ['error', 'media'])
437            ->where('log_time', '<', $timestamp)
438            ->delete();
439    }
440
441    /**
442     * @param int $max_age_in_seconds
443     *
444     * @return void
445     */
446    public function deleteOldSessions(int $max_age_in_seconds): void
447    {
448        $timestamp = Carbon::now()->subSeconds($max_age_in_seconds);
449
450        DB::table('session')
451            ->where('session_time', '<', $timestamp)
452            ->delete();
453    }
454
455    /**
456     * Delete a file or folder, if we can.
457     *
458     * @param FilesystemOperator $filesystem
459     * @param string             $path
460     *
461     * @return bool
462     */
463    private function deleteFileOrFolder(FilesystemOperator $filesystem, string $path): bool
464    {
465        if ($filesystem->fileExists($path)) {
466            try {
467                $filesystem->delete($path);
468            } catch (FilesystemException | UnableToDeleteFile $ex) {
469                try {
470                    $filesystem->deleteDirectory($path);
471                } catch (FilesystemException | UnableToDeleteDirectory $ex) {
472                    return false;
473                }
474            }
475        }
476
477        return true;
478    }
479}
480