xref: /webtrees/app/Services/HousekeepingService.php (revision 8fcd0d32e56ee262912bbdb593202cfd1cbc1615)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees\Services;
19
20use Exception;
21use Fisharebest\Webtrees\Database;
22use League\Flysystem\Filesystem;
23
24/**
25 * Clean up old data, files and folders.
26 */
27class HousekeepingService
28{
29    // This is a list of old files and directories, from earlier versions of webtrees.
30    // git diff 1.7.9..master --name-status | grep ^D
31    const OLD_PATHS = [
32        // Removed in 1.0.2
33        'language/en.mo',
34        // Removed in 1.0.3
35        'themechange.php',
36        // Removed in 1.1.0
37        'addremotelink.php',
38        'addsearchlink.php',
39        'client.php',
40        'dir_editor.php',
41        'editconfig_gedcom.php',
42        'editgedcoms.php',
43        'edit_merge.php',
44        'edit_news.php',
45        'genservice.php',
46        'logs.php',
47        'manageservers.php',
48        'media.php',
49        'module_admin.php',
50        //WT_ROOT.'modules', // Do not delete - users may have stored custom modules/data here
51        'opensearch.php',
52        'PEAR.php',
53        'pgv_to_wt.php',
54        'places',
55        //WT_ROOT.'robots.txt', // Do not delete this - it may contain user data
56        'serviceClientTest.php',
57        'siteconfig.php',
58        'SOAP',
59        'themes/clouds/mozilla.css',
60        'themes/clouds/netscape.css',
61        'themes/colors/mozilla.css',
62        'themes/colors/netscape.css',
63        'themes/fab/mozilla.css',
64        'themes/fab/netscape.css',
65        'themes/minimal/mozilla.css',
66        'themes/minimal/netscape.css',
67        'themes/webtrees/mozilla.css',
68        'themes/webtrees/netscape.css',
69        'themes/webtrees/style_rtl.css',
70        'themes/xenea/mozilla.css',
71        'themes/xenea/netscape.css',
72        'uploadmedia.php',
73        'useradmin.php',
74        'webservice',
75        'wtinfo.php',
76        // Removed in 1.1.2
77        'treenav.php',
78        // Removed in 1.2.0
79        'themes/clouds/jquery',
80        'themes/colors/jquery',
81        'themes/fab/jquery',
82        'themes/minimal/jquery',
83        'themes/webtrees/jquery',
84        'themes/xenea/jquery',
85        // Removed in 1.2.2
86        'themes/clouds/chrome.css',
87        'themes/clouds/opera.css',
88        'themes/clouds/print.css',
89        'themes/clouds/style_rtl.css',
90        'themes/colors/chrome.css',
91        'themes/colors/opera.css',
92        'themes/colors/print.css',
93        'themes/colors/style_rtl.css',
94        'themes/fab/chrome.css',
95        'themes/fab/opera.css',
96        'themes/minimal/chrome.css',
97        'themes/minimal/opera.css',
98        'themes/minimal/print.css',
99        'themes/minimal/style_rtl.css',
100        'themes/xenea/chrome.css',
101        'themes/xenea/opera.css',
102        'themes/xenea/print.css',
103        'themes/xenea/style_rtl.css',
104        // Removed in 1.2.3
105        //WT_ROOT.'modules_v2', // Do not delete - users may have stored custom modules/data here
106        // Removed in 1.2.4
107        'modules_v3/gedcom_favorites/help_text.php',
108        'modules_v3/GEDFact_assistant/_MEDIA/media_3_find.php',
109        'modules_v3/GEDFact_assistant/_MEDIA/media_3_search_add.php',
110        'modules_v3/GEDFact_assistant/_MEDIA/media_5_input.js',
111        'modules_v3/GEDFact_assistant/_MEDIA/media_5_input.php',
112        'modules_v3/GEDFact_assistant/_MEDIA/media_7_parse_addLinksTbl.php',
113        'modules_v3/GEDFact_assistant/_MEDIA/media_query_1a.php',
114        'modules_v3/GEDFact_assistant/_MEDIA/media_query_2a.php',
115        'modules_v3/GEDFact_assistant/_MEDIA/media_query_3a.php',
116        'modules_v3/lightbox/css/album_page_RTL2.css',
117        'modules_v3/lightbox/css/album_page_RTL.css',
118        'modules_v3/lightbox/css/album_page_RTL_ff.css',
119        'modules_v3/lightbox/css/clearbox_music.css',
120        'modules_v3/lightbox/css/clearbox_music_RTL.css',
121        'modules_v3/user_favorites/db_schema',
122        'modules_v3/user_favorites/help_text.php',
123        'search_engine.php',
124        'themes/clouds/modules.css',
125        'themes/colors/modules.css',
126        'themes/fab/modules.css',
127        'themes/minimal/modules.css',
128        'themes/webtrees/modules.css',
129        'themes/xenea/modules.css',
130        // Removed in 1.2.5
131        'modules_v3/clippings/index.php',
132        'modules_v3/googlemap/css/googlemap_style.css',
133        'modules_v3/googlemap/css/wt_v3_places_edit.css',
134        'modules_v3/googlemap/index.php',
135        'modules_v3/lightbox/index.php',
136        'modules_v3/recent_changes/help_text.php',
137        'modules_v3/todays_events/help_text.php',
138        'sidebar.php',
139        // Removed in 1.2.6
140        'modules_v3/sitemap/admin_index.php',
141        'modules_v3/sitemap/help_text.php',
142        'modules_v3/tree/css/styles',
143        'modules_v3/tree/css/treebottom.gif',
144        'modules_v3/tree/css/treebottomleft.gif',
145        'modules_v3/tree/css/treebottomright.gif',
146        'modules_v3/tree/css/tree.jpg',
147        'modules_v3/tree/css/treeleft.gif',
148        'modules_v3/tree/css/treeright.gif',
149        'modules_v3/tree/css/treetop.gif',
150        'modules_v3/tree/css/treetopleft.gif',
151        'modules_v3/tree/css/treetopright.gif',
152        'modules_v3/tree/css/treeview_print.css',
153        'modules_v3/tree/help_text.php',
154        'modules_v3/tree/images/print.png',
155        // Removed in 1.2.7
156        'login_register.php',
157        'modules_v3/top10_givnnames/help_text.php',
158        'modules_v3/top10_surnames/help_text.php',
159        // Removed in 1.3.0
160        'admin_site_ipaddress.php',
161        'downloadgedcom.php',
162        'export_gedcom.php',
163        'gedcheck.php',
164        'images',
165        'modules_v3/googlemap/admin_editconfig.php',
166        'modules_v3/googlemap/admin_placecheck.php',
167        'modules_v3/googlemap/flags.php',
168        'modules_v3/googlemap/images/pedigree_map.gif',
169        'modules_v3/googlemap/pedigree_map.php',
170        'modules_v3/lightbox/admin_config.php',
171        'modules_v3/lightbox/album.php',
172        'modules_v3/tree/css/vline.jpg',
173        // Removed in 1.3.1
174        'imageflush.php',
175        'modules_v3/googlemap/wt_v3_pedigree_map.js.php',
176        'modules_v3/lightbox/js/tip_balloon_RTL.js',
177        // Removed in 1.3.2
178        'modules_v3/address_report',
179        'modules_v3/lightbox/functions/lb_horiz_sort.php',
180        'modules_v3/random_media/help_text.php',
181        // Removed in 1.4.0
182        'imageview.php',
183        'media/MediaInfo.txt',
184        'media/thumbs/ThumbsInfo.txt',
185        'modules_v3/GEDFact_assistant/css/media_0_inverselink.css',
186        'modules_v3/lightbox/help_text.php',
187        'modules_v3/lightbox/images/blank.gif',
188        'modules_v3/lightbox/images/close_1.gif',
189        'modules_v3/lightbox/images/image_add.gif',
190        'modules_v3/lightbox/images/image_copy.gif',
191        'modules_v3/lightbox/images/image_delete.gif',
192        'modules_v3/lightbox/images/image_edit.gif',
193        'modules_v3/lightbox/images/image_link.gif',
194        'modules_v3/lightbox/images/images.gif',
195        'modules_v3/lightbox/images/image_view.gif',
196        'modules_v3/lightbox/images/loading.gif',
197        'modules_v3/lightbox/images/next.gif',
198        'modules_v3/lightbox/images/nextlabel.gif',
199        'modules_v3/lightbox/images/norm_2.gif',
200        'modules_v3/lightbox/images/overlay.png',
201        'modules_v3/lightbox/images/prev.gif',
202        'modules_v3/lightbox/images/prevlabel.gif',
203        'modules_v3/lightbox/images/private.gif',
204        'modules_v3/lightbox/images/slideshow.jpg',
205        'modules_v3/lightbox/images/transp80px.gif',
206        'modules_v3/lightbox/images/zoom_1.gif',
207        'modules_v3/lightbox/js',
208        'modules_v3/lightbox/music',
209        'modules_v3/lightbox/pic',
210        'themes/webtrees/chrome.css',
211        // Removed in 1.4.1
212        'modules_v3/lightbox/images/image_edit.png',
213        'modules_v3/lightbox/images/image_view.png',
214        // Removed in 1.4.2
215        'modules_v3/lightbox/images/image_view.png',
216        'modules_v3/top10_pageviews/help_text.php',
217        'themes/clouds/jquery-ui-1.10.0',
218        'themes/colors/jquery-ui-1.10.0',
219        'themes/fab/jquery-ui-1.10.0',
220        'themes/minimal/jquery-ui-1.10.0',
221        'themes/webtrees/jquery-ui-1.10.0',
222        'themes/xenea/jquery-ui-1.10.0',
223        // Removed in 1.5.0
224        'modules_v3/GEDFact_assistant/_CENS/census_note_decode.php',
225        'modules_v3/GEDFact_assistant/_CENS/census_asst_date.php',
226        'modules_v3/googlemap/wt_v3_googlemap.js.php',
227        'modules_v3/lightbox/functions/lightbox_print_media.php',
228        'modules_v3/upcoming_events/help_text.php',
229        'modules_v3/stories/help_text.php',
230        'modules_v3/user_messages/help_text.php',
231        'themes/clouds/favicon.png',
232        'themes/clouds/images',
233        'themes/clouds/msie.css',
234        'themes/clouds/style.css',
235        'themes/colors/css',
236        'themes/colors/favicon.png',
237        'themes/colors/images',
238        'themes/colors/ipad.css',
239        'themes/colors/msie.css',
240        'themes/fab/favicon.png',
241        'themes/fab/images',
242        'themes/fab/msie.css',
243        'themes/fab/style.css',
244        'themes/minimal/favicon.png',
245        'themes/minimal/images',
246        'themes/minimal/msie.css',
247        'themes/minimal/style.css',
248        'themes/webtrees/favicon.png',
249        'themes/webtrees/images',
250        'themes/webtrees/msie.css',
251        'themes/webtrees/style.css',
252        'themes/xenea/favicon.png',
253        'themes/xenea/images',
254        'themes/xenea/msie.css',
255        'themes/xenea/style.css',
256        // Removed in 1.5.1
257        'themes/clouds/css-1.5.0',
258        'themes/colors/css-1.5.0',
259        'themes/fab/css-1.5.0',
260        'themes/minimal/css-1.5.0',
261        'themes/webtrees/css-1.5.0',
262        'themes/xenea/css-1.5.0',
263        // Removed in 1.5.2
264        'themes/clouds/css-1.5.1',
265        'themes/colors/css-1.5.1',
266        'themes/fab/css-1.5.1',
267        'themes/minimal/css-1.5.1',
268        'themes/webtrees/css-1.5.1',
269        'themes/xenea/css-1.5.1',
270        // Removed in 1.5.3
271        'modules_v3/GEDFact_assistant/_CENS/census_asst_help.php',
272        'modules_v3/googlemap/admin_places.php',
273        'modules_v3/googlemap/defaultconfig.php',
274        'modules_v3/googlemap/googlemap.php',
275        'modules_v3/googlemap/placehierarchy.php',
276        'modules_v3/googlemap/places_edit.php',
277        'modules_v3/googlemap/util.js',
278        'modules_v3/googlemap/wt_v3_places_edit.js.php',
279        'modules_v3/googlemap/wt_v3_places_edit_overlays.js.php',
280        'modules_v3/googlemap/wt_v3_street_view.php',
281        'readme.html',
282        'themes/clouds/css-1.5.2',
283        'themes/colors/css-1.5.2',
284        'themes/fab/css-1.5.2',
285        'themes/minimal/css-1.5.2',
286        'themes/webtrees/css-1.5.2',
287        'themes/xenea/css-1.5.2',
288        // Removed in 1.6.0
289        'downloadbackup.php',
290        'modules_v3/ckeditor/ckeditor-4.3.2-custom',
291        'site-php-version.php',
292        'themes/clouds/css-1.5.3',
293        'themes/colors/css-1.5.3',
294        'themes/fab/css-1.5.3',
295        'themes/minimal/css-1.5.3',
296        'themes/webtrees/css-1.5.3',
297        'themes/xenea/css-1.5.3',
298        // Removed in 1.6.2
299        'themes/clouds/jquery-ui-1.10.3',
300        'themes/colors/css-1.6.0',
301        'themes/colors/jquery-ui-1.10.3',
302        'themes/fab/css-1.6.0',
303        'themes/fab/jquery-ui-1.10.3',
304        'themes/minimal/css-1.6.0',
305        'themes/minimal/jquery-ui-1.10.3',
306        'themes/webtrees/css-1.6.0',
307        'themes/webtrees/jquery-ui-1.10.3',
308        'themes/xenea/css-1.6.0',
309        'themes/xenea/jquery-ui-1.10.3',
310        // Removed in 1.7.0
311        'admin_site_other.php',
312        'js',
313        'language/en_GB.mo',
314        // Replaced with en-GB.mo
315        'language/en_US.mo',
316        // Replaced with en-US.mo
317        'language/pt_BR.mo',
318        // Replaced with pt-BR.mo
319        'language/zh_CN.mo',
320        // Replaced with zh-Hans.mo
321        'language/extra',
322        'library',
323        'modules_v3/batch_update/admin_batch_update.php',
324        'modules_v3/batch_update/plugins',
325        'modules_v3/charts/help_text.php',
326        'modules_v3/ckeditor/ckeditor-4.4.1-custom',
327        'modules_v3/clippings/clippings_ctrl.php',
328        'modules_v3/clippings/help_text.php',
329        'modules_v3/faq/help_text.php',
330        'modules_v3/gedcom_favorites/db_schema',
331        'modules_v3/gedcom_news/db_schema',
332        'modules_v3/googlemap/db_schema',
333        'modules_v3/googlemap/help_text.php',
334        'modules_v3/html/help_text.php',
335        'modules_v3/logged_in/help_text.php',
336        'modules_v3/review_changes/help_text.php',
337        'modules_v3/todo/help_text.php',
338        'modules_v3/tree/class_treeview.php',
339        'modules_v3/user_blog/db_schema',
340        'modules_v3/yahrzeit/help_text.php',
341        'save.php',
342        'themes/clouds/css-1.6.2',
343        'themes/clouds/templates',
344        'themes/clouds/header.php',
345        'themes/clouds/footer.php',
346        'themes/colors/css-1.6.2',
347        'themes/colors/templates',
348        'themes/colors/header.php',
349        'themes/colors/footer.php',
350        'themes/fab/css-1.6.2',
351        'themes/fab/templates',
352        'themes/fab/header.php',
353        'themes/fab/footer.php',
354        'themes/minimal/css-1.6.2',
355        'themes/minimal/templates',
356        'themes/minimal/header.php',
357        'themes/minimal/footer.php',
358        'themes/webtrees/css-1.6.2',
359        'themes/webtrees/templates',
360        'themes/webtrees/header.php',
361        'themes/webtrees/footer.php',
362        'themes/xenea/css-1.6.2',
363        'themes/xenea/templates',
364        'themes/xenea/header.php',
365        'themes/xenea/footer.php',
366        // Removed in 1.7.2
367        'assets/js-1.7.0',
368        // Removed in 1.7.3
369        'modules_v3/GEDFact_assistant/census/date.js',
370        'modules_v3/GEDFact_assistant/census/dynamicoptionlist.js',
371        // Removed in 1.7.4
372        'assets/js-1.7.2',
373        'themes/clouds/css-1.7.0',
374        'themes/colors/css-1.7.0',
375        'themes/fab/css-1.7.0',
376        'themes/minimal/css-1.7.0',
377        'themes/webtrees/css-1.7.0',
378        'themes/xenea/css-1.7.0',
379        // Removed in 1.7.5
380        'themes/clouds/css-1.7.4',
381        'themes/colors/css-1.7.4',
382        'themes/fab/css-1.7.4',
383        'themes/minimal/css-1.7.4',
384        'themes/webtrees/css-1.7.4',
385        'themes/xenea/css-1.7.4',
386        // Removed in 1.7.7
387        'assets/js-1.7.4',
388        'modules_v3/googlemap/images/css_sprite_facts.png',
389        'modules_v3/googlemap/images/flag_shadow.png',
390        'modules_v3/googlemap/images/shadow-left-large.png',
391        'modules_v3/googlemap/images/shadow-left-small.png',
392        'modules_v3/googlemap/images/shadow-right-large.png',
393        'modules_v3/googlemap/images/shadow-right-small.png',
394        'modules_v3/googlemap/images/shadow50.png',
395        'modules_v3/googlemap/images/transparent-left-large.png',
396        'modules_v3/googlemap/images/transparent-left-small.png',
397        'modules_v3/googlemap/images/transparent-right-large.png',
398        'modules_v3/googlemap/images/transparent-right-small.png',
399        // Removed in 1.7.8
400        'themes/clouds/css-1.7.5',
401        'themes/colors/css-1.7.5',
402        'themes/fab/css-1.7.5',
403        'themes/minimal/css-1.7.5',
404        'themes/webtrees/css-1.7.5',
405        'themes/xenea/css-1.7.5',
406        // Removed in 2.0.0
407        'action.php',
408        'addmedia.php',
409        'addmin.php',
410        'admin_media.php',
411        'admin_media_upload.php',
412        'admin_module_blocks.php',
413        'admin_module_charts.php',
414        'admin_module_menus.php',
415        'admin_module_reports.php',
416        'admin_module_sidebar.php',
417        'admin_module_tabs.php',
418        'admin_modules.php',
419        'admin_pgv_to_wt.php',
420        'admin_site_access.php',
421        'admin_site_change.php',
422        'admin_site_clean.php',
423        'admin_site_config.php',
424        'admin_site_info.php',
425        'admin_site_logs.php',
426        'admin_site_merge.php',
427        'admin_site_readme.php',
428        'admin_site_upgrade.php',
429        'admin_trees_check.php',
430        'admin_trees_config.php',
431        'admin_trees_download.php',
432        'admin_trees_duplicates.php',
433        'admin_trees_export.php',
434        'admin_trees_manage.php',
435        'admin_trees_merge.php',
436        'admin_trees_places.php',
437        'admin_trees_renumber.php',
438        'admin_trees_unconnected.php',
439        'admin_users.php',
440        'admin_users_bulk.php',
441        'ancestry.php',
442        'app/Controller',
443        'app/HitCounter.php',
444        'app/Module/ClippingsCart/ClippingsCartController.php',
445        'app/Module/FamiliesSidebarModule.php',
446        'app/Module/FamilyTreeFavorites',
447        'app/Module/GoogleMaps',
448        'app/Module/IndividualSidebarModule.php',
449        'app/Module/PageMenuModule.php',
450        'app/Query',
451        'app/SpecialChars',
452        'assets/js-1.7.7',
453        'assets/js-1.7.9',
454        'autocomplete.php',
455        'block_edit.php',
456        'branches.php',
457        'calendar.php',
458        'compact.php',
459        'data/html_purifier_cache',
460        'descendancy.php',
461        'editnews.php',
462        'edituser.php',
463        'edit_changes.php',
464        'edit_interface.php',
465        'expand_view.php',
466        'familybook.php',
467        'famlist.php',
468        'fanchart.php',
469        'find.php',
470        'help_text.php',
471        'hourglass.php',
472        'hourglass_ajax.php',
473        'import.php',
474        'includes',
475        'index_edit.php',
476        'indilist.php',
477        'inverselink.php',
478        'lifespan.php',
479        'login.php',
480        'logout.php',
481        'mediafirewall.php',
482        'medialist.php',
483        'message.php',
484        'module.php',
485        'notelist.php',
486        'packages',
487        'pedigree.php',
488        'relationship.php',
489        'repolist.php',
490        'reportengine.php',
491        'search.php',
492        'search_advanced.php',
493        'site-offline.php',
494        'site-unavailable.php',
495        'sourcelist.php',
496        'statistics.php',
497        'statisticsplot.php',
498        'themes/_administration',
499        'themes/_custom',
500        'themes/clouds/css-1.7.8',
501        'themes/clouds/jquery-ui-1.11.2',
502        'themes/colors/css-1.7.8',
503        'themes/colors/jquery-ui-1.11.2',
504        'themes/fab/css-1.7.8',
505        'themes/fab/jquery-ui-1.11.2',
506        'themes/minimal/css-1.7.8',
507        'themes/minimal/jquery-ui-1.11.2',
508        'themes/webtrees/css-1.7.8',
509        'themes/webtrees/jquery-ui-1.11.2',
510        'themes/xenea/css-1.7.8',
511        'themes/xenea/jquery-ui-1.11.2',
512        'timeline.php',
513    ];
514
515    /**
516     * Delete files and folders that belonged to an earlier version of webtrees.
517     * Return a list of those that we could not delete.
518     *
519     * @param Filesystem $filesystem
520     *
521     * @return array
522     */
523    public function deleteOldWebtreesFiles(Filesystem $filesystem): array
524    {
525        $paths_to_delete = [];
526
527        foreach (self::OLD_PATHS as $path) {
528            if (!$this->deleteFileOrFolder($filesystem, $path)) {
529                $paths_to_delete[] = $path;
530            }
531        }
532
533        return $paths_to_delete;
534    }
535
536    /**
537     * Delete old cache files.
538     *
539     * @param Filesystem $filesystem
540     * @param string     $path_to_cache
541     * @param int        $max_age_in_seconds
542     *
543     * @return void
544     */
545    public function deleteOldCacheFiles(Filesystem $filesystem, string $path_to_cache, int $max_age_in_seconds)
546    {
547        $list = $filesystem->listContents($path_to_cache, true);
548
549        foreach ($list as $metadata) {
550            if ($metadata['timestamp'] ?? WT_TIMESTAMP < WT_TIMESTAMP - $max_age_in_seconds) {
551                $this->deleteFileOrFolder($filesystem, $metadata['path']);
552            }
553        }
554    }
555
556    /**
557     * @param int $max_age_in_seconds
558     *
559     * @return void
560     */
561    public function deleteOldLogs(int $max_age_in_seconds)
562    {
563        if (Database::isConnected()) {
564            Database::prepare(
565                "DELETE FROM `##log` WHERE log_type IN ('error', 'media') AND log_time < FROM_UNIXTIME(:timestamp)"
566            )->execute([
567                'timestamp' => WT_TIMESTAMP - $max_age_in_seconds
568            ]);
569        }
570    }
571
572    /**
573     * @param int $max_age_in_seconds
574     *
575     * @return void
576     */
577    public function deleteOldSessions(int $max_age_in_seconds)
578    {
579        if (Database::isConnected()) {
580            Database::prepare(
581                "DELETE FROM `##session` WHERE session_time < FROM_UNIXTIME(:timestamp)"
582            )->execute([
583                'timestamp' => WT_TIMESTAMP - $max_age_in_seconds
584            ]);
585        }
586    }
587
588    /**
589     * Delete a file or folder, if we can.
590     *
591     * @param Filesystem $filesystem
592     * @param string     $path
593     *
594     * @return bool
595     */
596    private function deleteFileOrFolder(Filesystem $filesystem, string $path): bool
597    {
598        if ($filesystem->has($path)) {
599            try {
600                $metadata = $filesystem->getMetadata($path);
601
602                if ($metadata['type'] === 'dir') {
603                    $filesystem->deleteDir($path);
604                }
605
606                if ($metadata['type'] === 'file') {
607                    $filesystem->delete($path);
608                }
609            } catch (Exception $ex) {
610                return false;
611            }
612        }
613
614        return true;
615    }
616}
617