xref: /webtrees/app/Services/HousekeepingService.php (revision 32c8ac4d1651f27cb0b0cec5815a2e8067b9d764)
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    private 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        Database::prepare(
564            "DELETE FROM `##log` WHERE log_type IN ('error', 'media') AND log_time < FROM_UNIXTIME(:timestamp)"
565        )->execute([
566            'timestamp' => WT_TIMESTAMP - $max_age_in_seconds
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        Database::prepare(
578            "DELETE FROM `##session` WHERE session_time < FROM_UNIXTIME(:timestamp)"
579        )->execute([
580            'timestamp' => WT_TIMESTAMP - $max_age_in_seconds
581        ]);
582    }
583
584    /**
585     * Delete a file or folder, if we can.
586     *
587     * @param Filesystem $filesystem
588     * @param string     $path
589     *
590     * @return bool
591     */
592    private function deleteFileOrFolder(Filesystem $filesystem, string $path): bool
593    {
594        if ($filesystem->has($path)) {
595            try {
596                $metadata = $filesystem->getMetadata($path);
597
598                if ($metadata['type'] === 'dir') {
599                    $filesystem->deleteDir($path);
600                }
601
602                if ($metadata['type'] === 'file') {
603                    $filesystem->delete($path);
604                }
605            } catch (Exception $ex) {
606                return false;
607            }
608        }
609
610        return true;
611    }
612}
613