1#!/usr/local/bin/perl
2# --
3# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
4# --
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#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.txt.
17# --
18
19use strict;
20use warnings;
21
22use File::Basename;
23use FindBin qw($RealBin);
24use lib dirname($RealBin);
25use lib dirname($RealBin) . '/Kernel/cpan-lib';
26use lib dirname($RealBin) . '/Custom';
27
28use Kernel::System::Environment;
29use Kernel::System::VariableCheck qw( :all );
30
31use ExtUtils::MakeMaker;
32use File::Path;
33use Getopt::Long;
34use Term::ANSIColor;
35
36our %InstTypeToCMD = (
37
38    # [InstType] => {
39    #    CMD       => '[cmd to install module]',
40    #    UseModule => 1/0,
41    # }
42    # Set UseModule to 1 if you want to use the
43    # cpan module name of the package as replace string.
44    # e.g. yum install "perl(Date::Format)"
45    # If you set it 0 it will use the name
46    # for the InstType of the module
47    # e.g. apt-get install -y libtimedate-perl
48    # and as fallback the default cpan install command
49    # e.g. cpan DBD::Oracle
50    aptget => {
51        CMD       => 'apt-get install -y %s',
52        UseModule => 0,
53    },
54    emerge => {
55        CMD       => 'emerge %s',
56        UseModule => 0,
57    },
58    ppm => {
59        CMD       => 'ppm install %s',
60        UseModule => 0,
61    },
62    yum => {
63        CMD       => 'yum install "%s"',
64        SubCMD    => 'perl(%s)',
65        UseModule => 1,
66    },
67    zypper => {
68        CMD       => 'zypper install %s',
69        UseModule => 0,
70    },
71    ports => {
72        CMD       => 'cd /usr/ports %s',
73        SubCMD    => ' && make -C %s install clean',
74        UseModule => 0,
75    },
76    default => {
77        CMD => 'cpan %s',
78    },
79);
80
81our %DistToInstType = (
82
83    # apt-get
84    debian => 'aptget',
85    ubuntu => 'aptget',
86
87    # emerge
88    # for reasons unknown, some environments return "gentoo" (incl. the quotes)
89    '"gentoo"' => 'emerge',
90    gentoo     => 'emerge',
91
92    # yum
93    centos => 'yum',
94    fedora => 'yum',
95    rhel   => 'yum',
96    redhat => 'yum',
97
98    # zypper
99    suse => 'zypper',
100
101    # FreeBSD
102    freebsd => 'ports',
103);
104
105our $OSDist;
106eval {
107    require Linux::Distribution;    ## nofilter(TidyAll::Plugin::OTRS::Perl::Require)
108    import Linux::Distribution;
109    $OSDist = Linux::Distribution::distribution_name() || '';
110};
111if ( !defined $OSDist ) {
112    $OSDist = $^O;
113}
114
115my $AllModules;
116my $PackageList;
117my $Help;
118GetOptions(
119    all  => \$AllModules,
120    list => \$PackageList,
121    h    => \$Help
122);
123
124# check needed params
125if ($Help) {
126    print "\nReturn all required and optional packages of OTRS.\n\n";
127    print "Usage:\n";
128    print " otrs.CheckModules.pl [-list|all]\n\n";
129    print "Options:\n";
130    printf " %-22s - %s", '[-list]', 'Return an install command with all required packages.' . "\n";
131    printf " %-22s - %s", '[-all]',  'Return all required, optional and bundled packages of OTRS.' . "\n\n";
132    exit 1;
133}
134
135my $Options = shift || '';
136my $NoColors;
137
138if ( $ENV{nocolors} || $Options =~ m{\A nocolors}msxi ) {
139    $NoColors = 1;
140}
141
142my $ExitCode = 0;    # success
143
144# config
145my @NeededModules = (
146    {
147        Module    => 'Apache::DBI',
148        Required  => 0,
149        Comment   => 'Improves Performance on Apache webservers with mod_perl enabled.',
150        InstTypes => {
151            aptget => 'libapache-dbi-perl',
152            emerge => 'dev-perl/Apache-DBI',
153            zypper => 'perl-Apache-DBI',
154            ports  => 'www/p5-Apache-DBI',
155        },
156    },
157    {
158        Module    => 'Apache2::Reload',
159        Required  => 0,
160        Comment   => 'Avoids web server restarts on mod_perl.',
161        InstTypes => {
162            aptget => 'libapache2-mod-perl2',
163            emerge => 'dev-perl/Apache-Reload',
164            zypper => 'apache2-mod_perl',
165            ports  => 'www/mod_perl2',
166        },
167    },
168    {
169        Module    => 'Archive::Tar',
170        Required  => 1,
171        Comment   => 'Required for compressed file generation (in perlcore).',
172        InstTypes => {
173            emerge => 'perl-core/Archive-Tar',
174            zypper => 'perl-Archive-Tar',
175            ports  => 'archivers/p5-Archive-Tar',
176        },
177    },
178    {
179        Module    => 'Archive::Zip',                               # required for OTRSBusiness
180        Required  => 1,
181        Comment   => 'Required for compressed file generation.',
182        InstTypes => {
183            aptget => 'libarchive-zip-perl',
184            emerge => 'dev-perl/Archive-Zip',
185            zypper => 'perl-Archive-Zip',
186            ports  => 'archivers/p5-Archive-Zip',
187        },
188    },
189    {
190        Module    => 'Crypt::Eksblowfish::Bcrypt',
191        Required  => 0,
192        Comment   => 'For strong password hashing.',
193        InstTypes => {
194            aptget => 'libcrypt-eksblowfish-perl',
195            emerge => 'dev-perl/Crypt-Eksblowfish',
196            zypper => 'perl-Crypt-Eksblowfish',
197            ports  => 'security/p5-Crypt-Eksblowfish',
198        },
199    },
200    {
201        Module    => 'Date::Format',
202        Required  => 1,
203        InstTypes => {
204            aptget => 'libtimedate-perl',
205            emerge => 'dev-perl/TimeDate',
206            zypper => 'perl-TimeDate',
207            ports  => 'devel/p5-TimeDate',
208        },
209    },
210    {
211        Module    => 'DateTime',
212        Required  => 1,
213        InstTypes => {
214            aptget => 'libdatetime-perl',
215            emerge => 'dev-perl/DateTime',
216            zypper => 'perl-DateTime',
217            ports  => 'devel/p5-TimeDate',
218        },
219        Depends => [
220            {
221                Module              => 'DateTime::TimeZone',
222                Comment             => 'Olson time zone database, required for correct time calculations.',
223                VersionsRecommended => [
224                    {
225                        Version => '2.20',
226                        Comment => 'This version includes recent time zone changes for Chile.',
227                    },
228                ],
229            },
230        ],
231    },
232    {
233        Module    => 'DBI',
234        Required  => 1,
235        InstTypes => {
236            aptget => 'libdbi-perl',
237            emerge => 'dev-perl/DBI',
238            zypper => 'perl-DBI',
239            ports  => 'databases/p5-DBI',
240        },
241    },
242    {
243        Module    => 'DBD::mysql',
244        Required  => 0,
245        Comment   => 'Required to connect to a MySQL database.',
246        InstTypes => {
247            aptget => 'libdbd-mysql-perl',
248            emerge => 'dev-perl/DBD-mysql',
249            zypper => 'perl-DBD-mysql',
250            ports  => 'databases/p5-DBD-mysql',
251        },
252    },
253    {
254        Module               => 'DBD::ODBC',
255        Required             => 0,
256        VersionsNotSupported => [
257            {
258                Version => '1.23',
259                Comment =>
260                    'This version is broken and not useable! Please upgrade to a higher version.',
261            },
262        ],
263        Comment   => 'Required to connect to a MS-SQL database.',
264        InstTypes => {
265            aptget => 'libdbd-odbc-perl',
266            emerge => undef,
267            yum    => undef,
268            zypper => undef,
269            ports  => 'databases/p5-DBD-ODBC',
270        },
271    },
272    {
273        Module    => 'DBD::Oracle',
274        Required  => 0,
275        Comment   => 'Required to connect to a Oracle database.',
276        InstTypes => {
277            aptget => undef,
278            emerge => undef,
279            yum    => undef,
280            zypper => undef,
281            ports  => undef,
282        },
283    },
284    {
285        Module    => 'DBD::Pg',
286        Required  => 0,
287        Comment   => 'Required to connect to a PostgreSQL database.',
288        InstTypes => {
289            aptget => 'libdbd-pg-perl',
290            emerge => 'dev-perl/DBD-Pg',
291            zypper => 'perl-DBD-Pg',
292            ports  => 'databases/p5-DBD-Pg',
293        },
294    },
295    {
296        Module    => 'Digest::SHA',    # Supposed to be in perlcore, but seems to be missing on some distributions.
297        Required  => 1,
298        InstTypes => {
299            aptget => 'libdigest-sha-perl',
300            emerge => 'dev-perl/Digest-SHA',
301            zypper => 'perl-Digest-SHA',
302            ports  => 'security/p5-Digest-SHA'
303        },
304    },
305    {
306        Module          => 'Encode::HanExtra',
307        VersionRequired => '0.23',
308        Required        => 0,
309        Comment         => 'Required to handle mails with several Chinese character sets.',
310        InstTypes       => {
311            aptget => 'libencode-hanextra-perl',
312            emerge => 'dev-perl/Encode-HanExtra',
313            zypper => 'perl-Encode-HanExtra',
314            ports  => 'chinese/p5-Encode-HanExtra',
315        },
316    },
317    {
318        Module              => 'IO::Socket::SSL',
319        Required            => 0,
320        Comment             => 'Required for SSL connections to web and mail servers.',
321        VersionsRecommended => [
322            {
323                Version => '2.066',
324                Comment => 'This version fixes email sending (bug#14357).',
325            },
326        ],
327        InstTypes => {
328            aptget => 'libio-socket-ssl-perl',
329            emerge => 'dev-perl/IO-Socket-SSL',
330            zypper => 'perl-IO-Socket-SSL',
331            ports  => 'security/p5-IO-Socket-SSL',
332        },
333    },
334    {
335        Module    => 'JSON::XS',
336        Required  => 0,
337        Comment   => 'Recommended for faster AJAX/JavaScript handling.',
338        InstTypes => {
339            aptget => 'libjson-xs-perl',
340            emerge => 'dev-perl/JSON-XS',
341            zypper => 'perl-JSON-XS',
342            ports  => 'converters/p5-JSON-XS',
343        },
344    },
345    {
346        Module   => 'List::Util::XS',
347        Required => 1,
348        Comment =>
349            "Do a 'force install Scalar::Util' via cpan shell to fix this problem. Please make sure to have an c compiler and make installed before.",
350        InstTypes => {
351            aptget => 'libscalar-list-utils-perl',
352            emerge => 'perl-core/Scalar-List-Utils',
353            zypper => 'perl-Scalar-List-Utils',
354            ports  => 'lang/p5-Scalar-List-Utils',
355        },
356    },
357    {
358        Module    => 'LWP::UserAgent',
359        Required  => 1,
360        InstTypes => {
361            aptget => 'libwww-perl',
362            emerge => 'dev-perl/libwww-perl',
363            zypper => 'perl-libwww-perl',
364            ports  => 'www/p5-libwww',
365        },
366    },
367    {
368        Module          => 'Mail::IMAPClient',
369        VersionRequired => '3.22',
370        Comment         => 'Required for IMAP TLS connections.',
371        Required        => 0,
372        InstTypes       => {
373            aptget => 'libmail-imapclient-perl',
374            emerge => 'dev-perl/Mail-IMAPClient',
375            zypper => 'perl-Mail-IMAPClient',
376            ports  => 'mail/p5-Mail-IMAPClient',
377        },
378        Depends => [
379            {
380                Module              => 'IO::Socket::SSL',
381                Required            => 0,
382                Comment             => 'Required for SSL connections to web and mail servers.',
383                VersionsRecommended => [
384                    {
385                        Version => '2.066',
386                        Comment => 'This version fixes email sending (bug#14357).',
387                    },
388                ],
389                InstTypes => {
390                    aptget => 'libio-socket-ssl-perl',
391                    emerge => 'dev-perl/IO-Socket-SSL',
392                    zypper => 'perl-IO-Socket-SSL',
393                    ports  => 'security/p5-IO-Socket-SSL',
394                },
395            },
396            {
397                Module    => 'Authen::SASL',
398                Required  => 0,
399                Comment   => 'Required for MD5 authentication mechanisms in IMAP connections.',
400                InstTypes => {
401                    aptget => 'libauthen-sasl-perl',
402                    emerge => 'dev-perl/Authen-SASL',
403                    zypper => 'perl-Authen-SASL',
404                },
405            },
406            {
407                Module    => 'Authen::NTLM',
408                Required  => 0,
409                Comment   => 'Required for NTLM authentication mechanism in IMAP connections.',
410                InstTypes => {
411                    aptget => 'libauthen-ntlm-perl',
412                    emerge => 'dev-perl/Authen-NTLM',
413                    zypper => 'perl-Authen-NTLM',
414                },
415            },
416        ],
417    },
418    {
419        Module    => 'ModPerl::Util',
420        Required  => 0,
421        Comment   => 'Improves Performance on Apache webservers dramatically.',
422        InstTypes => {
423            aptget => 'libapache2-mod-perl2',
424            emerge => 'www-apache/mod_perl',
425            zypper => 'apache2-mod_perl',
426            ports  => 'www/mod_perl2',
427        },
428    },
429    {
430        Module    => 'Moo',
431        Required  => 1,
432        Comment   => 'Required for random number generator.',
433        InstTypes => {
434            aptget => 'libmoo-perl',
435            emerge => 'dev-perl/Moo',
436            zypper => 'perl-Moo',
437            ports  => 'devel/p5-Moo',
438        },
439    },
440    {
441        Module               => 'Net::DNS',
442        Required             => 1,
443        VersionsNotSupported => [
444            {
445                Version => '0.60',
446                Comment =>
447                    'This version is broken and not useable! Please upgrade to a higher version.',
448            },
449        ],
450        InstTypes => {
451            aptget => 'libnet-dns-perl',
452            emerge => 'dev-perl/Net-DNS',
453            zypper => 'perl-Net-DNS',
454            ports  => 'dns/p5-Net-DNS',
455        },
456    },
457    {
458        Module    => 'Net::LDAP',
459        Required  => 0,
460        Comment   => 'Required for directory authentication.',
461        InstTypes => {
462            aptget => 'libnet-ldap-perl',
463            emerge => 'dev-perl/perl-ldap',
464            zypper => 'perl-ldap',
465            ports  => 'net/p5-perl-ldap',
466        },
467    },
468    {
469        Module              => 'Net::SMTP',
470        Required            => 0,
471        Comment             => 'Simple Mail Transfer Protocol Client.',
472        VersionsRecommended => [
473            {
474                Version => '3.11',
475                Comment => 'This version fixes email sending (bug#14357).',
476            },
477        ],
478        InstTypes => {
479            aptget => undef,
480            emerge => undef,
481            zypper => undef,
482            ports  => undef,
483        },
484    },
485    {
486        Module    => 'Template',
487        Required  => 1,
488        Comment   => 'Template::Toolkit, the rendering engine of OTRS.',
489        InstTypes => {
490            aptget => 'libtemplate-perl',
491            emerge => 'dev-perl/Template-Toolkit',
492            zypper => 'perl-Template-Toolkit',
493            ports  => 'www/p5-Template-Toolkit',
494        },
495    },
496    {
497        Module    => 'Template::Stash::XS',
498        Required  => 1,
499        Comment   => 'The fast data stash for Template::Toolkit.',
500        InstTypes => {
501            aptget => 'libtemplate-perl',
502            emerge => 'dev-perl/Template-Toolkit',
503            zypper => 'perl-Template-Toolkit',
504            ports  => 'www/p5-Template-Toolkit',
505        },
506    },
507    {
508        Module    => 'Text::CSV_XS',
509        Required  => 0,
510        Comment   => 'Recommended for faster CSV handling.',
511        InstTypes => {
512            aptget => 'libtext-csv-xs-perl',
513            emerge => 'dev-perl/Text-CSV_XS',
514            zypper => 'perl-Text-CSV_XS',
515            ports  => 'textproc/p5-Text-CSV_XS',
516        },
517    },
518    {
519        Module    => 'Time::HiRes',
520        Required  => 1,
521        Comment   => 'Required for high resolution timestamps.',
522        InstTypes => {
523            aptget => 'perl',
524            emerge => 'perl-core/Time-HiRes',
525            zypper => 'perl-Time-HiRes',
526            ports  => 'devel/p5-Time-HiRes',
527        },
528    },
529    {
530        Module    => 'XML::LibXML',
531        Required  => 1,
532        Comment   => 'Required for XML processing.',
533        InstTypes => {
534            aptget => 'libxml-libxml-perl',
535            zypper => 'perl-XML-LibXML',
536            ports  => 'textproc/p5-XML-LibXML',
537        },
538    },
539    {
540        Module    => 'XML::LibXSLT',
541        Required  => 0,
542        Comment   => 'Required for Generic Interface XSLT mapping module.',
543        InstTypes => {
544            aptget => 'libxml-libxslt-perl',
545            zypper => 'perl-XML-LibXSLT',
546            ports  => 'textproc/p5-XML-LibXSLT',
547        },
548    },
549    {
550        Module    => 'XML::Parser',
551        Required  => 0,
552        Comment   => 'Recommended for XML processing.',
553        InstTypes => {
554            aptget => 'libxml-parser-perl',
555            emerge => 'dev-perl/XML-Parser',
556            zypper => 'perl-XML-Parser',
557            ports  => 'textproc/p5-XML-Parser',
558        },
559    },
560    {
561        Module   => 'YAML::XS',
562        Required => 1,
563        Comment  => 'Required for fast YAML processing.',
564
565        # Example of how to use VersionsRecommended option.
566        # VersionsRecommended => [
567        #     {
568        #         Version => '5.0.1',
569        #         Comment => 'This version fixes a bug.',
570        #     },
571        #     {
572        #         Version => '6.0.1',
573        #         Comment => 'This version fixes a critical issue.',
574        #     },
575        # ],
576        InstTypes => {
577            aptget => 'libyaml-libyaml-perl',
578            emerge => 'dev-perl/YAML-LibYAML',
579            zypper => 'perl-YAML-LibYAML',
580            ports  => 'textproc/p5-YAML-LibYAML',
581        },
582    },
583);
584
585if ($PackageList) {
586    my %PackageList = _PackageList( \@NeededModules );
587
588    if ( IsArrayRefWithData( $PackageList{Packages} ) ) {
589
590        my $CMD = $PackageList{CMD};
591
592        for my $Package ( @{ $PackageList{Packages} } ) {
593            if ( $PackageList{SubCMD} ) {
594                $Package = sprintf $PackageList{SubCMD}, $Package;
595            }
596        }
597        printf $CMD, join( ' ', @{ $PackageList{Packages} } );
598        print "\n";
599    }
600}
601else {
602    # try to determine module version number
603    my $Depends = 0;
604
605    for my $Module (@NeededModules) {
606        _Check( $Module, $Depends, $NoColors );
607    }
608
609    if ($AllModules) {
610        print "\nBundled modules:\n\n";
611
612        my %PerlInfo = Kernel::System::Environment->PerlInfoGet(
613            BundledModules => 1,
614        );
615
616        for my $Module ( sort keys %{ $PerlInfo{Modules} } ) {
617            _Check(
618                {
619                    Module   => $Module,
620                    Required => 1,
621                },
622                $Depends,
623                $NoColors
624            );
625        }
626    }
627}
628
629sub _Check {
630    my ( $Module, $Depends, $NoColors ) = @_;
631
632    print "  " x ( $Depends + 1 );
633    print "o $Module->{Module}";
634    my $Length = 33 - ( length( $Module->{Module} ) + ( $Depends * 2 ) );
635    print '.' x $Length;
636
637    my $Version = Kernel::System::Environment->ModuleVersionGet( Module => $Module->{Module} );
638    if ($Version) {
639
640        # cleanup version number
641        my $CleanedVersion = _VersionClean(
642            Version => $Version,
643        );
644
645        my $ErrorMessage;
646
647        # Test if all module dependencies are installed by requiring the module.
648        #   Don't do this for Net::DNS as it seems to take very long (>20s) in a
649        #   mod_perl environment sometimes.
650        my %DontRequire = (
651            'Net::DNS'        => 1,
652            'Email::Valid'    => 1,    # uses Net::DNS internally
653            'Apache2::Reload' => 1,    # is not needed / working on systems without mod_perl (like Plack etc.)
654        );
655
656        ## no critic
657        if ( !$DontRequire{ $Module->{Module} } && !eval "require $Module->{Module}" ) {
658            $ErrorMessage .= 'Not all prerequisites for this module correctly installed. ';
659        }
660        ## use critic
661
662        if ( $Module->{VersionsNotSupported} ) {
663
664            my $VersionsNotSupported = 0;
665            ITEM:
666            for my $Item ( @{ $Module->{VersionsNotSupported} } ) {
667
668                # cleanup item version number
669                my $ItemVersion = _VersionClean(
670                    Version => $Item->{Version},
671                );
672
673                if ( $CleanedVersion == $ItemVersion ) {
674                    $VersionsNotSupported = $Item->{Comment};
675                    last ITEM;
676                }
677            }
678
679            if ($VersionsNotSupported) {
680                $ErrorMessage .= "Version $Version not supported! $VersionsNotSupported ";
681            }
682        }
683
684        my $AdditionalText = '';
685
686        if ( $Module->{VersionsRecommended} ) {
687
688            my $VersionsRecommended = 0;
689            ITEM:
690            for my $Item ( @{ $Module->{VersionsRecommended} } ) {
691
692                my $ItemVersion = _VersionClean(
693                    Version => $Item->{Version},
694                );
695
696                if ( $CleanedVersion < $ItemVersion ) {
697                    $AdditionalText
698                        .= "    Please consider updating to version $Item->{Version} or higher: $Item->{Comment}\n";
699                }
700            }
701        }
702
703        if ( $Module->{VersionRequired} ) {
704
705            # cleanup item version number
706            my $RequiredModuleVersion = _VersionClean(
707                Version => $Module->{VersionRequired},
708            );
709
710            if ( $CleanedVersion < $RequiredModuleVersion ) {
711                $ErrorMessage
712                    .= "Version $Version installed but $Module->{VersionRequired} or higher is required! ";
713            }
714        }
715
716        if ($ErrorMessage) {
717            if ($NoColors) {
718                print "FAILED! $ErrorMessage\n";
719            }
720            else {
721                print color('red') . 'FAILED!' . color('reset') . " $ErrorMessage\n";
722            }
723            $ExitCode = 1;    # error
724        }
725        else {
726            my $OutputVersion = $Version;
727
728            if ( $OutputVersion =~ m{ [0-9.] }xms ) {
729                $OutputVersion = 'v' . $OutputVersion;
730            }
731
732            if ($NoColors) {
733                print "ok ($OutputVersion)\n$AdditionalText";
734            }
735            else {
736                print color('green') . 'ok'
737                    . color('reset')
738                    . " ($OutputVersion)\n"
739                    . color('yellow')
740                    . "$AdditionalText"
741                    . color('reset');
742            }
743        }
744    }
745    else {
746        my $Comment  = $Module->{Comment} ? ' - ' . $Module->{Comment} : '';
747        my $Required = $Module->{Required};
748        my $Color    = 'yellow';
749
750        # OS Install Command
751        my %InstallCommand = _GetInstallCommand($Module);
752
753        # create example installation string for module
754        my $InstallText = '';
755        if ( IsHashRefWithData( \%InstallCommand ) ) {
756            my $CMD = $InstallCommand{CMD};
757            if ( $InstallCommand{SubCMD} ) {
758                $CMD = sprintf $InstallCommand{CMD}, $InstallCommand{SubCMD};
759            }
760
761            $InstallText = " To install, you can use: '" . sprintf( $CMD, $InstallCommand{Package} ) . "'.";
762        }
763
764        if ($Required) {
765            $Required = 'required';
766            $Color    = 'red';
767            $ExitCode = 1;            # error
768        }
769        else {
770            $Required = 'optional';
771        }
772        if ($NoColors) {
773            print "Not installed! ($Required $Comment)\n";
774        }
775        else {
776            print color($Color)
777                . 'Not installed!'
778                . color('reset')
779                . "$InstallText ($Required$Comment)\n";
780        }
781    }
782
783    if ( $Module->{Depends} ) {
784        for my $ModuleSub ( @{ $Module->{Depends} } ) {
785            _Check( $ModuleSub, $Depends + 1, $NoColors );
786        }
787    }
788
789    return 1;
790}
791
792sub _PackageList {
793    my ($PackageList) = @_;
794
795    my $CMD;
796    my $SubCMD;
797    my @Packages;
798
799    # if we're on Windows we don't need to see Apache + mod_perl modules
800    MODULE:
801    for my $Module ( @{$PackageList} ) {
802
803        my $Required = $Module->{Required};
804        my $Version  = Kernel::System::Environment->ModuleVersionGet( Module => $Module->{Module} );
805        if ( !$Version ) {
806            my %InstallCommand = _GetInstallCommand($Module);
807
808            if ( $Module->{Depends} ) {
809
810                MODULESUB:
811                for my $ModuleSub ( @{ $Module->{Depends} } ) {
812                    my $Required          = $Module->{Required};
813                    my %InstallCommandSub = _GetInstallCommand($ModuleSub);
814
815                    next MODULESUB if !IsHashRefWithData( \%InstallCommandSub );
816                    next MODULESUB if !$Required;
817
818                    push @Packages, $InstallCommandSub{Package};
819                }
820            }
821
822            next MODULE if !IsHashRefWithData( \%InstallCommand );
823            next MODULE if !$Required;
824
825            $CMD    = $InstallCommand{CMD};
826            $SubCMD = $InstallCommand{SubCMD};
827            push @Packages, $InstallCommand{Package};
828        }
829    }
830
831    return (
832        CMD      => $CMD,
833        SubCMD   => $SubCMD,
834        Packages => \@Packages,
835    );
836}
837
838sub _VersionClean {
839    my (%Param) = @_;
840
841    return 0 if !$Param{Version};
842    return 0 if $Param{Version} eq 'undef';
843
844    # replace all special characters with an dot
845    $Param{Version} =~ s{ [_-] }{.}xmsg;
846
847    my @VersionParts = split q{\.}, $Param{Version};
848
849    my $CleanedVersion = '';
850    for my $Count ( 0 .. 4 ) {
851        $VersionParts[$Count] ||= 0;
852        $CleanedVersion .= sprintf "%04d", $VersionParts[$Count];
853    }
854
855    return int $CleanedVersion;
856}
857
858sub _GetInstallCommand {
859    my ($Module) = @_;
860    my $CMD;
861    my $SubCMD;
862    my $Package;
863
864    # returns the installation type e.g. ppm
865    my $InstType     = $DistToInstType{$OSDist};
866    my $OuputInstall = 1;
867
868    if ($InstType) {
869
870        # gets the install command for installation type
871        # e.g. ppm install %s
872        # default is the cpan install command
873        # e.g. cpan %s
874        $CMD    = $InstTypeToCMD{$InstType}->{CMD};
875        $SubCMD = $InstTypeToCMD{$InstType}->{SubCMD};
876
877        # gets the target package
878        if (
879            exists $Module->{InstTypes}->{$InstType}
880            && !defined $Module->{InstTypes}->{$InstType}
881            )
882        {
883            # if we a hash key for the installation type but a undefined value
884            # then we prevent the output for the installation command
885            $OuputInstall = 0;
886        }
887        elsif ( $InstTypeToCMD{$InstType}->{UseModule} ) {
888
889            # default is the cpan module name
890            $Package = $Module->{Module};
891        }
892        else {
893            # if the package name is defined for the installation type
894            # e.g. ppm then we use this as package name
895            $Package = $Module->{InstTypes}->{$InstType};
896        }
897    }
898
899    return if !$OuputInstall;
900
901    if ( !$CMD || !$Package ) {
902        $CMD     = $InstTypeToCMD{default}->{CMD};
903        $SubCMD  = $InstTypeToCMD{default}->{SubCMD};
904        $Package = $Module->{Module};
905    }
906
907    return (
908        CMD     => $CMD,
909        SubCMD  => $SubCMD,
910        Package => $Package,
911    );
912}
913
914exit $ExitCode;
915