1use strict;
2use warnings;
3
4use Config;
5use ExtUtils::MakeMaker;
6use File::Basename qw(dirname);
7use Getopt::Long;
8
9eval "use ExtUtils::MakeMaker::Coverage";
10$@ or print "Adding testcover target\n";
11
12use vars qw($opt_default $opt_libpath $opt_static);
13
14GetOptions(
15    "default", \$opt_default,
16    "lib=s",   \$opt_libpath,
17    "static",  \$opt_static,
18);
19
20$opt_default ||= $ENV{CRYPT_SSLEAY_DEFAULT};
21
22# FIND POSSIBLE SSL INSTALLATIONS
23
24my @POSSIBLE_SSL_DIRS;
25if ($opt_libpath) {
26    # explicit from command-line
27    @POSSIBLE_SSL_DIRS = ($opt_libpath);
28    $opt_default       = 1;
29}
30elsif ($^O eq 'MSWin32') {
31    @POSSIBLE_SSL_DIRS = 'c:\\openssl';
32}
33elsif ($^O eq 'VMS') {
34    @POSSIBLE_SSL_DIRS = '/ssl$root';
35}
36else {
37    # Unix and the rest
38    @POSSIBLE_SSL_DIRS = qw(
39        /local
40        /local/ssl
41        /opt/ssl
42        /usr
43        /usr/local
44        /usr/local/ssl
45        /usr/local/openssl
46    );
47}
48
49my @CANDIDATE = do {
50    my %seen;
51    grep {!$seen{$_->{check}}++}
52        map {Candidate($_)}
53            @POSSIBLE_SSL_DIRS
54};
55
56if (@CANDIDATE == 0) {
57    $opt_default = 0;
58    my $bar = '=' x 72;
59    print <<"INFO";
60$bar
61No installed SSL libraries found in any of the following places.
62INFO
63    print "    $_\n" for @POSSIBLE_SSL_DIRS;
64    print <<"INFO";
65You will have to either specify a directory location at the following
66prompt, or rerun the Makefile.PL program and use the --lib switch
67to specify the path. If the path in question is considered standard
68on your platform, please consider filing a bug report in order to
69have it taken into account in a subsequent version of Crypt::SSLeay.
70
71INFO
72
73    if (-f '/etc/debian_version') {
74        print <<"DEBIAN_INFO";
75
76This host looks like it is running Debian. Crypt::SSLeay needs to
77be compiled with C headers that the libssl-dev package makes
78available. Please install that package before trying to build this
79module. (You can always deinstall the package afterwards, once
80Crypt::SSLeay has been built).
81DEBIAN_INFO
82    }
83}
84
85my $SSL_DIR;
86my $pkg_config;
87
88if (@CANDIDATE == 1) {
89    $pkg_config = $CANDIDATE[0];
90    if ($opt_default) {
91        $SSL_DIR = $pkg_config->{dir};
92    }
93    else {
94        print <<"INFO";
95=======================================================
96Only one $pkg_config->{type} installation found at $pkg_config->{dir}
97Consider running 'perl Makefile.PL --default' the next
98time Crypt::SSLeay is upgraded to select this directory
99automatically thereby avoiding the following prompt.
100=======================================================
101INFO
102    }
103}
104elsif (@CANDIDATE > 1) {
105    print "Found multiple possibilities for OpenSSL\n";
106    for my $c (@CANDIDATE) {
107        print "  $c->{dir} ($c->{type} $c->{ver})\n";
108    }
109}
110
111if (not $SSL_DIR) {
112    my %cand;
113    for my $c (@CANDIDATE) {
114        $cand{$c->{dir}} = {%$c};
115    }
116
117    $SSL_DIR = prompt "Which SSL install path do you want to use?",
118        $CANDIDATE[0]->{dir};
119
120    if (exists $cand{$SSL_DIR}) {
121        # we've already determined that this directory is usable
122        $pkg_config = $cand{$SSL_DIR};
123    }
124    else {
125        # unknown directory, better check it out
126        $pkg_config = Candidate($SSL_DIR);
127        if (not $pkg_config) {
128            die <<"INFO";
129$SSL_DIR does not appear to be an SSL library installation, since
130the required header files were not found. The build cannot proceed.
131INFO
132        }
133    }
134}
135
136# note: $SSL_DIR is now sane at this point
137
138my (@INC_FLAGS, @LIB_FLAGS);
139
140if ($^O eq 'VMS') {
141    push @INC_FLAGS, "-I$pkg_config->{inc}";
142    push @LIB_FLAGS, qw(-L/SYS$SHARE -lSSL$LIBSSL_SHR32 -lSSL$LIBCRYPTO_SHR32);
143}
144elsif ($^O eq 'MSWin32') {
145    # external tools probably expect \ and not / for path separators
146    $SSL_DIR =~ tr{/}{\\};
147
148    # default to drive C: if no drive or relative path
149    $SSL_DIR = "c:$SSL_DIR" if $SSL_DIR !~ m{\A(?:[a-z]:|\.\.[\\/])}i;
150
151    my $inc = $pkg_config->{inc};
152    $inc =~ tr{/}{\\};
153    $inc = "c:$inc" if $inc !~ m{\A(?:[a-z]:|\.\.[\\/])}i;
154    push @INC_FLAGS, "-I$inc";
155    push @INC_FLAGS, "-I$SSL_DIR\\inc32" if -d "$SSL_DIR/inc32";
156
157    my $vanilla = $Config{cc} eq 'gcc' ? 1 : 0;
158    $vanilla and print "Assuming Vanilla/Strawberry Perl installation\n";
159
160    if ($vanilla and -d "$SSL_DIR\\lib\\MinGW") {
161        push @LIB_FLAGS, "-L$SSL_DIR\\lib\\MinGW";
162    }
163    elsif(-d "$SSL_DIR/lib") {
164        push @LIB_FLAGS, "-L$SSL_DIR\\lib";
165    }
166    else {
167        my $dir = $opt_static ? "$SSL_DIR\\out32" : "$SSL_DIR\\out32dll";
168        if (-d $dir) {
169            push @LIB_FLAGS, "-L$dir";
170        }
171        else {
172            # Allow developers to point at OpenSSL source...
173            push @LIB_FLAGS, "-L$SSL_DIR";
174        }
175    }
176
177    push @LIB_FLAGS, qw(-lssleay32 -llibeay32);
178    push @LIB_FLAGS, qw(-lRSAglue -lrsaref) if $pkg_config->{type} ne 'OpenSSL';
179}
180else {
181    push @INC_FLAGS, "-I$pkg_config->{inc}";
182
183    push @LIB_FLAGS, "-L$pkg_config->{lib}", qw(-lssl -lcrypto -lgcc);
184    push @LIB_FLAGS, qw(-lRSAglue -lrsaref) if $pkg_config->{type} ne 'OpenSSL';
185
186    # ccc on alpha support
187    if ($^O eq 'linux'
188        and `uname -m` =~ /alpha/
189        and !(system("nm $SSL_DIR/lib/libssl.a|grep -q 'U _Ots'")>>8)
190    ) {
191        push @LIB_FLAGS, '-lots';
192    }
193
194    # this fix was suggested for building on RedHat 9
195    push @INC_FLAGS, '-I/usr/kerberos/include' if -d '/usr/kerberos/include';
196}
197
198# write include file that determines ssl support
199# we need to include crypto.h for SSLeay so the version gets picked up in SSLeay.xs
200
201open(INCLUDE, ">crypt_ssleay_version.h") || die("can't open crypt_ssleay_version.h for writing: $!");
202print INCLUDE <<"INFO";
203#include "$pkg_config->{prefix}ssl.h"
204#include "$pkg_config->{prefix}crypto.h"
205#include "$pkg_config->{prefix}err.h"
206#include "$pkg_config->{prefix}rand.h"
207#include "$pkg_config->{prefix}pkcs12.h"
208
209INFO
210
211if ($] < 5.005) {
212    print "adding PL_sv_undef symbol for this ancient perl installation";
213    print INCLUDE <<"INFO";
214/* defining PL_sv_undef for very old perls ($]) */
215#ifndef PL_sv_undef
216#define PL_sv_undef sv_undef
217#endif
218
219INFO
220}
221
222if ($pkg_config->{type} eq 'OpenSSL') {
223    # OPENSSL_free defined in OpenSSL 0.9.6 and higher
224    if ($pkg_config->{ver} =~ /\b0\.9\.[2-5]/) {
225        print INCLUDE "#define CRYPT_SSLEAY_free free\n";
226    }
227    else {
228        print INCLUDE "#define CRYPT_SSLEAY_free OPENSSL_free\n";
229    }
230}
231else {
232    print INCLUDE "#define CRYPT_SSLEAY_free free\n";
233}
234
235close INCLUDE or die "Cannot close crypt_ssleay_version.h for output: $!\n";
236
237print <<"INFO";
238
239BUILD INFORMATION
240================================================
241ssl library: $pkg_config->{type} $pkg_config->{ver} in $SSL_DIR
242ssl header:  $pkg_config->{prefix}ssl.h
243libraries:   @LIB_FLAGS
244include dir: @INC_FLAGS
245================================================
246INFO
247
248my @license =
249    do {
250        my $version = $ExtUtils::MakeMaker::VERSION;
251        $version =~ tr/_//d;
252        $version} > 6.30
253    ? qw(LICENSE perl)
254    : ();
255
256WriteMakefile(
257    NAME          => 'Crypt::SSLeay',
258    AUTHOR        => 'David Landgren',
259    ABSTRACT_FROM => 'SSLeay.pm',
260    VERSION_FROM  => 'SSLeay.pm',
261    LIBS          => ["@LIB_FLAGS"],
262    INC           => "@INC_FLAGS",
263    NEEDS_LINKING => 1,
264    clean         => {
265        FILES => 'crypt_ssleay_version.h test.config',
266    },
267    @license,
268);
269
270if (open OUT, '> test.config') {
271    print OUT <<"INFO";
272ssl $pkg_config->{type} $pkg_config->{ver} in $SSL_DIR
273lib @LIB_FLAGS
274inc @INC_FLAGS
275cc $Config{cc}
276INFO
277
278    print <<"INFO";
279The test suite can attempt to connect to public servers
280to ensure that the code is working properly. If you are
281behind a strict firewall or have no network connectivity,
282these tests may fail (through no fault of the code).
283
284INFO
285    my $network_tests = prompt
286        "Do you want to run the live tests (y/N) ?",
287        'N';
288    print OUT "network_tests ", ($network_tests =~ /y/i) ? 1 : 0, "\n";
289    close OUT;
290}
291
292## HELPERS
293
294sub Candidate {
295    my $dir = shift;
296    return unless -d $dir;
297
298    my $inc_dir;
299    my $version_file;
300    for (
301         "$dir/inc32/openssl/opensslv.h", # old win32 builds
302         "$dir/crypto/opensslv.h", # cygwin32 builds
303         "$dir/include/openssl/opensslv.h",
304         "$dir/../include/openssl/opensslv.h", # Solaris
305         "$dir/include/opensslv.h",
306         "$dir/include/crypto.h"
307    ) {
308        if (-e $_) {
309            $version_file = $_;
310            last;
311        }
312    }
313    return unless defined $version_file;
314    my $fingerprint = join(':', (stat $version_file)[0,1]);
315
316    my $open_ssl = ($version_file =~ /openssl/i) ? 1 : 0;
317    $inc_dir = dirname($version_file);
318    return unless -e "$inc_dir/ssl.h";
319
320    my $prefix;
321    if ($^O eq 'MSWin32') {
322        $inc_dir =~ s{[\\/]openssl\z}{};
323        $prefix  = 'openssl/';
324    }
325    elsif (index($inc_dir, '/../') > -1) {
326        # OpenSSL include directory is in a sibling directory
327        $inc_dir =~ s{\/openssl\z}{};
328        $prefix  = 'openssl/';
329    }
330    else {
331        $prefix = ($inc_dir =~ /\bopenssl/i) ? 'openssl/' : '';
332    }
333
334    open(VERSION_FILE, $version_file) or return;
335    my $version_match = $open_ssl ? "OPENSSL_VERSION_NUMBER" : "SSLEAY_VERSION_NUMBER";
336    my $version;
337    my $type;
338    while (<VERSION_FILE>) {
339        if (/^#define\s+$version_match\s+0x0+(\d\d\d)/) {
340            $version = $1;
341            $version =~ s/(\d)0(\d)/$1$2/;
342            $type = ($version > 92) ? "OpenSSL" : "SSLeay";
343            $version = join('.', split(//, "0$version"));
344            last;
345        }
346    }
347    close(VERSION_FILE);
348
349    # Silly test to look for the library files
350    my $found_lib;
351    my $libd;
352    my $subdir = $opt_static ? 'out32' : 'out32dll';
353    if (-d "$dir/$subdir") {
354        $libd = [$subdir];
355    }
356    elsif ($^O eq 'MSWin32' and $Config{cc} eq 'gcc') {
357        $libd = ['lib/MinGW'];
358    }
359    else {
360        # second attempt is for Solaris, like the include directory, the
361        # library directory may be in a sibling directory
362        $libd = ['lib', '../lib'];
363    }
364
365    SCAN:
366    for my $d (@$libd) {
367        my $lib_dir = "$dir/$d";
368        if (opendir(LIBDIR, $lib_dir)) {
369            while (defined($_ = readdir(LIBDIR))) {
370                if (/\A(?:lib(?:crypto|eay32|ssl)|ssleay32)/) {
371                    $found_lib = $lib_dir;
372                    last SCAN;
373                }
374            }
375            closedir(LIBDIR);
376        }
377    }
378
379    if (!$found_lib) {
380        my @tried = join( ',' => map {"$dir/$_"} @$libd);
381        print "Did not locate expected SSL library files in @tried\n";
382    }
383
384    return {
385        dir    => $dir,
386        inc    => $inc_dir,
387        lib    => $found_lib,
388        ver    => $version,
389        type   => $type,
390        prefix => $prefix,
391        check  => $fingerprint,
392    };
393}
394
395