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