1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.hazendaz.maven.makeself;
19
20 import java.io.BufferedReader;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.nio.charset.StandardCharsets;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.attribute.PosixFilePermission;
29 import java.nio.file.attribute.PosixFilePermissions;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35
36 import javax.inject.Inject;
37
38 import org.apache.commons.io.FilenameUtils;
39 import org.apache.maven.plugin.MojoExecutionException;
40 import org.apache.maven.plugin.MojoFailureException;
41 import org.apache.maven.plugins.annotations.LifecyclePhase;
42 import org.apache.maven.plugins.annotations.Mojo;
43 import org.apache.maven.plugins.annotations.Parameter;
44 import org.apache.maven.project.MavenProject;
45 import org.apache.maven.project.MavenProjectHelper;
46
47
48
49
50 @Mojo(name = "makeself", defaultPhase = LifecyclePhase.VERIFY, requiresProject = false)
51 public class MakeselfMojo extends AbstractGitMojo {
52
53
54 private static final String PERMISSIONS = "rwxr-xr--";
55
56
57 private static final boolean ATTACH_ARTIFACT = true;
58
59
60
61
62 @Parameter(defaultValue = "makeself", property = "archiveDir", required = true)
63 private String archiveDir;
64
65
66
67
68 @Parameter(defaultValue = "makeself.sh", property = "fileName", required = true)
69 private String fileName;
70
71
72
73
74 @Parameter(defaultValue = "Makeself self-extractable archive", property = "label", required = true)
75 private String label;
76
77
78
79
80
81
82 @Parameter(defaultValue = "./makeself.sh", property = "startupScript", required = true)
83 private String startupScript;
84
85
86
87
88
89
90
91
92 @Parameter(defaultValue = "sh", property = "extension")
93 private String extension;
94
95
96
97
98
99
100
101
102 @Parameter(property = "classifier")
103 private String classifier;
104
105
106
107
108
109
110
111
112
113 @Parameter(property = "inlineScript")
114 private boolean inlineScript;
115
116
117
118
119
120
121
122
123
124
125
126
127
128 @Parameter(property = "scriptArgs")
129 private List<String> scriptArgs;
130
131
132
133
134
135
136 @Parameter(property = "version")
137 private Boolean version;
138
139
140
141
142 @Parameter(property = "help")
143 private Boolean help;
144
145
146
147
148
149
150 @Parameter(property = "tarQuietly")
151 private Boolean tarQuietly;
152
153
154
155
156
157
158 @Parameter(property = "quiet")
159 private Boolean quiet;
160
161
162
163
164 @Parameter(property = "gzip")
165 private Boolean gzip;
166
167
168
169
170
171
172 @Parameter(property = "bzip2")
173 private Boolean bzip2;
174
175
176
177
178
179
180
181
182 @Parameter(property = "bzip3")
183 private Boolean bzip3;
184
185
186
187
188
189
190 @Parameter(property = "pbzip2")
191 private Boolean pbzip2;
192
193
194
195
196
197
198 @Parameter(property = "xz")
199 private Boolean xz;
200
201
202
203
204
205
206 @Parameter(property = "lzo")
207 private Boolean lzo;
208
209
210
211
212
213
214 @Parameter(property = "lz4")
215 private Boolean lz4;
216
217
218
219
220 @Parameter(property = "zstd")
221 private Boolean zstd;
222
223
224
225
226 @Parameter(property = "pigz")
227 private Boolean pigz;
228
229
230
231
232 @Parameter(property = "base64")
233 private Boolean base64;
234
235
236
237
238
239 @Parameter(property = "gpgEncrypt")
240 private Boolean gpgEncrypt;
241
242
243
244
245 @Parameter(property = "gpgAsymmetricEncryptSign")
246 private Boolean gpgAsymmetricEncryptSign;
247
248
249
250
251
252 @Parameter(property = "sslEncrypt")
253 private Boolean sslEncrypt;
254
255
256
257
258 @Parameter(property = "sslPasswd")
259 private String sslPasswd;
260
261
262
263
264
265
266 @Parameter(property = "sslPassSrc")
267 private String sslPassSrc;
268
269
270
271
272 @Parameter(property = "sslNoMd")
273 private Boolean sslNoMd;
274
275
276
277
278
279 @Parameter(property = "compress")
280 private Boolean compress;
281
282
283
284
285 @Parameter(property = "complevel")
286 private Integer complevel;
287
288
289
290
291 @Parameter(property = "compExtra")
292 private String compExtra;
293
294
295
296
297
298
299 @Parameter(property = "nochown")
300 private Boolean nochown;
301
302
303
304
305
306
307 @Parameter(property = "chown")
308 private Boolean chown;
309
310
311
312
313 @Parameter(property = "nocomp")
314 private Boolean nocomp;
315
316
317
318
319
320
321
322 @Parameter(property = "threads")
323 private Integer threads;
324
325
326
327
328
329
330 @Parameter(property = "notemp")
331 private Boolean notemp;
332
333
334
335
336
337
338 @Parameter(property = "needroot")
339 private Boolean needroot;
340
341
342
343
344
345 @Parameter(property = "current")
346 private Boolean current;
347
348
349
350
351
352 @Parameter(property = "follow")
353 private Boolean follow;
354
355
356
357
358
359
360 @Parameter(property = "noprogress")
361 private Boolean noprogress;
362
363
364
365
366
367
368 @Parameter(property = "append")
369 private Boolean append;
370
371
372
373
374
375
376 @Parameter(property = "headerFile")
377 private String headerFile;
378
379
380
381
382
383
384
385 @Parameter(property = "preextractScript")
386 private String preextractScript;
387
388
389
390
391
392 @Parameter(property = "cleanupScript")
393 private String cleanupScript;
394
395
396
397
398
399
400
401 @Parameter(property = "copy")
402 private Boolean copy;
403
404
405 @Parameter(property = "nox11")
406 private Boolean nox11;
407
408
409 @Parameter(property = "nowait")
410 private Boolean nowait;
411
412
413
414
415
416 @Parameter(property = "nomd5")
417 private Boolean nomd5;
418
419
420
421
422
423 @Parameter(property = "nocrc")
424 private Boolean nocrc;
425
426
427
428
429 @Parameter(property = "sha256")
430 private Boolean sha256;
431
432
433
434
435
436
437 @Parameter(property = "signPassphrase")
438 private String signPassphrase;
439
440
441
442
443
444
445 @Parameter(property = "lsmFile")
446 private String lsmFile;
447
448
449
450
451 @Parameter(property = "gpgExtraOpt")
452 private String gpgExtraOpt;
453
454
455
456
457
458 @Parameter(property = "tarFormatOpt")
459 private String tarFormatOpt;
460
461
462
463
464
465
466
467 @Parameter(property = "tarExtraOpt")
468 private String tarExtraOpt;
469
470
471
472
473 @Parameter(property = "untarExtraOpt")
474 private String untarExtraOpt;
475
476
477
478
479
480
481
482 private String extractTargetDir;
483
484
485
486
487
488 @Parameter(property = "keepUmask")
489 private Boolean keepUmask;
490
491
492
493
494 @Parameter(property = "exportConf")
495 private Boolean exportConf;
496
497
498
499
500 @Parameter(property = "packagingDate")
501 private String packagingDate;
502
503
504
505
506 @Parameter(property = "licenseFile")
507 private String licenseFile;
508
509
510
511
512 @Parameter(property = "nooverwrite")
513 private Boolean nooverwrite;
514
515
516
517
518 @Parameter(property = "helpHeaderFile")
519 private String helpHeaderFile;
520
521
522 @Parameter(defaultValue = "false", property = "makeself.skip")
523 private boolean skip;
524
525
526 @Parameter(defaultValue = "false", property = "autoRun")
527 private boolean autoRun;
528
529
530 @Parameter(defaultValue = "${project.build.directory}/", readonly = true)
531 private String buildTarget;
532
533
534 @Parameter(defaultValue = "${project.build.directory}/makeself-tmp/", readonly = true)
535 private File makeselfTempDirectory;
536
537
538 @Inject
539 private MavenProjectHelper projectHelper;
540
541
542 @Parameter(defaultValue = "${project}", readonly = true, required = true)
543 private MavenProject project;
544
545
546 private Path makeself;
547
548 @Override
549 public void execute() throws MojoExecutionException, MojoFailureException {
550
551 if (this.gitPath == null) {
552 this.gitPath = "";
553 }
554
555
556 if (this.skip) {
557 this.getLog().info("Makeself is skipped");
558 return;
559 }
560
561
562 Path path = Path.of(this.buildTarget.concat(this.archiveDir));
563 if (!Files.exists(path)) {
564 throw new MojoExecutionException("ArchiveDir: missing '" + this.buildTarget.concat(this.archiveDir) + "'");
565 }
566
567
568 if (this.inlineScript) {
569
570 if (this.scriptArgs == null) {
571 throw new MojoExecutionException("ScriptArgs required when running inlineScript");
572 }
573 } else {
574
575 if (!this.startupScript.startsWith("./")) {
576 throw new MojoExecutionException("StartupScript required to start with './'");
577 }
578
579
580 path = Path.of(this.buildTarget.concat(this.archiveDir).concat(this.startupScript.substring(1)));
581 if (!Files.exists(path)) {
582 throw new MojoExecutionException("StartupScript: missing '"
583 + this.buildTarget.concat(this.archiveDir).concat(this.startupScript.substring(1)) + "'");
584 }
585 }
586
587
588 this.extractMakeself();
589
590
591 if (this.isWindows()) {
592 if (!this.gitPath.isEmpty() && Files.exists(Path.of(this.gitPath))) {
593 this.getLog().debug("Using existing 'Git' found at " + this.gitPath);
594 this.gitPath = this.gitPath + AbstractGitMojo.GIT_USER_BIN;
595 } else {
596 this.checkGitSetup();
597 }
598 } else {
599
600 this.gitPath = "";
601 }
602
603 try {
604
605 this.getLog().debug("Execute Bash Version");
606 this.execute(Arrays.asList(this.gitPath + "bash", "--version"), !MakeselfMojo.ATTACH_ARTIFACT);
607
608
609 this.getLog().debug("Execute Makeself Version");
610 this.execute(Arrays.asList(this.gitPath + "bash", this.makeself.toAbsolutePath().toString(), "--version"),
611 !MakeselfMojo.ATTACH_ARTIFACT);
612
613
614 if (this.isTrue(this.version)) {
615 return;
616 }
617
618
619 if (this.isTrue(this.help)) {
620 this.getLog().debug("Execute Makeself Help");
621 this.execute(Arrays.asList(this.gitPath + "bash", this.makeself.toAbsolutePath().toString(), "--help"),
622 !MakeselfMojo.ATTACH_ARTIFACT);
623 return;
624 }
625
626
627 this.getLog().debug("Loading Makeself Basic Configuration");
628 final List<String> target = new ArrayList<>(
629 Arrays.asList(this.gitPath + "bash", this.makeself.toAbsolutePath().toString()));
630 target.addAll(this.loadArgs());
631 target.add(this.buildTarget.concat(this.archiveDir));
632 target.add(this.buildTarget.concat(this.fileName));
633 target.add(this.label);
634 target.add(this.startupScript);
635 if (this.scriptArgs != null) {
636 target.addAll(this.scriptArgs);
637 }
638
639
640 this.getLog().info("Running makeself build");
641
642
643 this.getLog().debug("Execute Makeself Build");
644 this.execute(target, MakeselfMojo.ATTACH_ARTIFACT);
645
646
647 this.getLog().debug("Execute Makeself Info on Resulting Shell Script");
648 this.execute(Arrays.asList(this.gitPath + "bash", this.buildTarget.concat(this.fileName), "--info"),
649 !MakeselfMojo.ATTACH_ARTIFACT);
650
651
652 if (!this.isWindows()) {
653 this.getLog().debug("Execute Makeself List on Resulting Shell Script");
654 this.execute(Arrays.asList(this.gitPath + "bash", this.buildTarget.concat(this.fileName), "--list"),
655 !MakeselfMojo.ATTACH_ARTIFACT);
656 }
657
658
659 if (this.autoRun) {
660 this.getLog().info("Auto-run created shell (this may take a few minutes)");
661 this.execute(Arrays.asList(this.gitPath + "bash", this.buildTarget.concat(this.fileName)),
662 !MakeselfMojo.ATTACH_ARTIFACT);
663 }
664 } catch (final IOException e) {
665 this.getLog().error("", e);
666 } catch (final InterruptedException e) {
667 this.getLog().error("", e);
668
669 Thread.currentThread().interrupt();
670 }
671 }
672
673 private void execute(final List<String> target, final boolean attach) throws IOException, InterruptedException {
674
675
676 this.getLog().debug("Execution commands: " + target);
677
678
679 final ProcessBuilder processBuilder = new ProcessBuilder(target);
680 processBuilder.redirectErrorStream(true);
681
682
683 if (this.isWindows()) {
684 final Map<String, String> envs = processBuilder.environment();
685 this.getLog().debug("Environment Variables: " + envs);
686
687 if (this.portableGit == null) {
688
689 final String location = this.gitPath;
690
691 if (envs.get("Path") != null) {
692 envs.put("Path", location + ";" + envs.get("Path"));
693 this.getLog().debug("Environment Path Variable: " + envs.get("Path"));
694 } else if (envs.get("PATH") != null) {
695
696 envs.put("PATH", location + ";" + envs.get("PATH").replace("Program Files", "\"Program Files\""));
697 this.getLog().debug("Environment Path Variable: " + envs.get("PATH"));
698 }
699 } else {
700
701 final String location = this.repoSession.getLocalRepository().getBasedir() + File.separator
702 + this.portableGit.getName() + File.separator + this.portableGit.getVersion();
703
704 if (envs.get("Path") != null) {
705 envs.put("Path", location + "/usr/bin;" + envs.get("Path"));
706 this.getLog().debug("Environment Path Variable: " + envs.get("Path"));
707 } else if (envs.get("PATH") != null) {
708
709 envs.put("PATH",
710 location + "/usr/bin;" + envs.get("PATH").replace("Program Files", "\"Program Files\""));
711 this.getLog().debug("Environment Path Variable: " + envs.get("PATH"));
712 }
713 }
714 }
715
716
717 final Process process = processBuilder.start();
718
719
720 try (BufferedReader reader = new BufferedReader(
721 new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
722 String line = "";
723 while ((line = reader.readLine()) != null) {
724 this.getLog().info(line);
725 }
726 this.getLog().info("");
727 }
728
729
730 final int status = process.waitFor();
731 if (status > 0) {
732 this.getLog().error(String.join(" ", "makeself failed with error status:", String.valueOf(status)));
733 }
734
735
736 if (status == 0 && attach) {
737 this.projectHelper.attachArtifact(this.project, this.extension, this.classifier,
738 Path.of(this.buildTarget, FilenameUtils.getName(this.fileName)).toFile());
739 }
740 }
741
742
743
744
745 private void extractMakeself() {
746 this.getLog().debug("Extracting Makeself");
747
748
749 final Path makeselfTemp = Path.of(this.makeselfTempDirectory.getAbsolutePath());
750 if (!Files.exists(makeselfTemp) && !makeselfTemp.toFile().mkdirs()) {
751 this.getLog()
752 .error(String.join(" ", "Unable to make directory", this.makeselfTempDirectory.getAbsolutePath()));
753 return;
754 }
755 this.getLog().debug(String.join(" ", "Created directory for", this.makeselfTempDirectory.getAbsolutePath()));
756
757 final ClassLoader classloader = this.getClass().getClassLoader();
758
759
760 this.makeself = this.makeselfTempDirectory.toPath().resolve("makeself.sh");
761 if (!Files.exists(this.makeself)) {
762 this.getLog().debug("Writing makeself.sh");
763 try (InputStream link = classloader.getResourceAsStream("META-INF/makeself/makeself.sh")) {
764 final Path path = this.makeself.toAbsolutePath();
765 Files.copy(link, path);
766 this.setFilePermissions(this.makeself.toFile());
767 this.setPosixFilePermissions(path);
768 } catch (final IOException e) {
769 this.getLog().error("", e);
770 }
771 }
772
773
774 final Path makeselfHeader = this.makeselfTempDirectory.toPath().resolve("makeself-header.sh");
775 if (!Files.exists(makeselfHeader)) {
776 this.getLog().debug("Writing makeself-header.sh");
777 try (InputStream link = classloader.getResourceAsStream("META-INF/makeself/makeself-header.sh")) {
778 final Path path = makeselfHeader.toAbsolutePath();
779 Files.copy(link, path);
780 this.setFilePermissions(makeselfHeader.toFile());
781 this.setPosixFilePermissions(path);
782 } catch (final IOException e) {
783 this.getLog().error("", e);
784 }
785 }
786 }
787
788 private void setFilePermissions(final File file) {
789 if (!file.setExecutable(true, true)) {
790 this.getLog().error(String.join(" ", "Unable to set executable:", file.getName()));
791 } else {
792 this.getLog().debug(String.join(" ", "Set executable for", file.getName()));
793 }
794 }
795
796 private void setPosixFilePermissions(final Path path) {
797 final Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(MakeselfMojo.PERMISSIONS);
798
799 try {
800 Files.setPosixFilePermissions(path, permissions);
801 this.getLog().debug(String.join(" ", "Set Posix File Permissions for", path.toString(), "as",
802 MakeselfMojo.PERMISSIONS));
803 } catch (final IOException e) {
804 this.getLog().error("Failed attempted Posix permissions", e);
805 } catch (final UnsupportedOperationException e) {
806
807 this.getLog().debug("Failed attempted Posix permissions", e);
808 }
809 }
810
811
812
813
814
815
816 private List<String> loadArgs() {
817 this.getLog().debug("Loading arguments");
818
819 final List<String> args = new ArrayList<>(50);
820
821
822 if (this.isTrue(this.tarQuietly)) {
823 args.add("--tar-quietly");
824 }
825
826
827 if (this.isTrue(this.quiet)) {
828 args.add("--quiet");
829 }
830
831
832 if (this.isTrue(this.gzip)) {
833 args.add("--gzip");
834 }
835
836
837
838
839 if (this.isTrue(this.bzip2)) {
840 args.add("--bzip2");
841 }
842
843
844
845
846 if (this.isTrue(this.bzip3)) {
847 args.add("--bzip3");
848 }
849
850
851
852
853 if (this.isTrue(this.pbzip2)) {
854 args.add("--pbzip2");
855 }
856
857
858
859
860 if (this.isTrue(this.xz)) {
861 args.add("--xz");
862 }
863
864
865
866
867 if (this.isTrue(this.lzo)) {
868 args.add("--lzo");
869 }
870
871
872
873
874 if (this.isTrue(this.lz4)) {
875 args.add("--lz4");
876 }
877
878
879 if (this.isTrue(this.zstd)) {
880 args.add("--zstd");
881 }
882
883
884 if (this.isTrue(this.pigz)) {
885 args.add("--pigz");
886 }
887
888
889 if (this.isTrue(this.base64)) {
890 args.add("--base64");
891 }
892
893
894
895 if (this.isTrue(this.gpgEncrypt)) {
896 args.add("--gpg-encrypt");
897 }
898
899
900 if (this.isTrue(this.gpgAsymmetricEncryptSign)) {
901 args.add("--gpg-asymmetric-encrypt-sign");
902 }
903
904
905
906 if (this.isTrue(this.sslEncrypt)) {
907 args.add("--ssl-encrypt");
908 }
909
910
911 if (this.sslPasswd != null) {
912 args.add("--ssl-passwd");
913 args.add(this.sslPasswd);
914 }
915
916
917
918
919 if (this.sslPassSrc != null) {
920 args.add("--ssl-pass-src");
921 args.add(this.sslPassSrc);
922 }
923
924
925 if (this.isTrue(this.sslNoMd)) {
926 args.add("--ssl-no-md");
927 }
928
929
930
931 if (this.isTrue(this.compress)) {
932 args.add("--compress");
933 }
934
935
936 if (this.complevel != null) {
937 args.add("--complevel");
938 args.add(this.complevel.toString());
939 }
940
941
942 if (this.compExtra != null) {
943 args.add("--comp-extra");
944 args.add(this.compExtra);
945 }
946
947
948 if (this.isTrue(this.nochown)) {
949 args.add("--nochown");
950 }
951
952
953 if (this.isTrue(this.chown)) {
954 args.add("--chown");
955 }
956
957
958 if (this.isTrue(this.nocomp)) {
959 args.add("--nocomp");
960 }
961
962
963
964
965
966 if (this.threads != null) {
967 args.add("--threads");
968 args.add(this.threads.toString());
969 }
970
971
972
973
974 if (this.isTrue(this.notemp)) {
975 args.add("--notemp");
976 }
977
978
979 if (this.isTrue(this.needroot)) {
980 args.add("--needroot");
981 }
982
983
984
985 if (this.isTrue(this.current)) {
986 args.add("--current");
987 }
988
989
990
991 if (this.isTrue(this.follow)) {
992 args.add("--follow");
993 }
994
995
996 if (this.isTrue(this.noprogress)) {
997 args.add("--noprogress");
998 }
999
1000
1001
1002
1003 if (this.isTrue(this.append)) {
1004 args.add("--append");
1005 }
1006
1007
1008
1009
1010 if (this.headerFile != null) {
1011 args.add("--header");
1012 args.add(this.headerFile);
1013 }
1014
1015
1016
1017 if (this.preextractScript != null) {
1018 args.add("--preextract");
1019 args.add(this.preextractScript);
1020 }
1021
1022
1023
1024 if (this.cleanupScript != null) {
1025 args.add("--cleanup");
1026 args.add(this.cleanupScript);
1027 }
1028
1029
1030
1031
1032
1033 if (this.isTrue(this.copy)) {
1034 args.add("--copy");
1035 }
1036
1037
1038 if (this.isTrue(this.nox11)) {
1039 args.add("--nox11");
1040 }
1041
1042
1043 if (this.isTrue(this.nowait)) {
1044 args.add("--nowait");
1045 }
1046
1047
1048
1049 if (this.isTrue(this.nomd5)) {
1050 args.add("--nomd5");
1051 }
1052
1053
1054
1055 if (this.isTrue(this.nocrc)) {
1056 args.add("--nocrc");
1057 }
1058
1059
1060 if (this.isTrue(this.sha256)) {
1061 args.add("--sha256");
1062 }
1063
1064
1065
1066
1067
1068 if (this.lsmFile != null) {
1069 args.add("--lsm");
1070 args.add(this.lsmFile);
1071 }
1072
1073
1074 if (this.gpgExtraOpt != null) {
1075 args.add("--gpg-extra");
1076 args.add(this.gpgExtraOpt);
1077 }
1078
1079
1080
1081 if (this.tarFormatOpt != null) {
1082 args.add("--tar-format");
1083 args.add(this.tarFormatOpt);
1084 }
1085
1086
1087
1088
1089 if (this.tarExtraOpt != null) {
1090 args.add("--tar-extra");
1091 args.add(this.tarExtraOpt);
1092 }
1093
1094
1095 if (this.untarExtraOpt != null) {
1096 args.add("--untar-extra");
1097 args.add(this.untarExtraOpt);
1098 }
1099
1100
1101 if (this.signPassphrase != null) {
1102 args.add("--sign");
1103 args.add(this.signPassphrase);
1104 }
1105
1106
1107
1108 if (this.extractTargetDir != null) {
1109 args.add("--target");
1110 args.add(this.extractTargetDir);
1111 }
1112
1113
1114
1115 if (this.isTrue(this.keepUmask)) {
1116 args.add("--keep-umask");
1117 }
1118
1119
1120 if (this.isTrue(this.exportConf)) {
1121 args.add("--export-conf");
1122 }
1123
1124
1125 if (this.packagingDate != null) {
1126 args.add("--packaging-date");
1127 args.add(this.packagingDate);
1128 }
1129
1130
1131 if (this.licenseFile != null) {
1132 args.add("--license");
1133 args.add(this.licenseFile);
1134 }
1135
1136
1137 if (this.isTrue(this.nooverwrite)) {
1138 args.add("--nooverwrite");
1139 }
1140
1141
1142 if (this.helpHeaderFile != null) {
1143 args.add("--help-header");
1144 args.add(this.helpHeaderFile);
1145 }
1146
1147 return args;
1148 }
1149
1150 private boolean isTrue(final Boolean value) {
1151 if (value != null) {
1152 return value.booleanValue();
1153 }
1154 return false;
1155 }
1156
1157 }