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