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