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