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