Home | History | Annotate | Line # | Download | only in OpenSSL
      1  1.1  christos #! /usr/bin/env perl
      2  1.1  christos # Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
      3  1.1  christos #
      4  1.1  christos # Licensed under the Apache License 2.0 (the "License").  You may not use
      5  1.1  christos # this file except in compliance with the License.  You can obtain a copy
      6  1.1  christos # in the file LICENSE in the source distribution or at
      7  1.1  christos # https://www.openssl.org/source/license.html
      8  1.1  christos 
      9  1.1  christos package OpenSSL::Util;
     10  1.1  christos 
     11  1.1  christos use strict;
     12  1.1  christos use warnings;
     13  1.1  christos use Carp;
     14  1.1  christos 
     15  1.1  christos use Exporter;
     16  1.1  christos use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
     17  1.1  christos $VERSION = "0.1";
     18  1.1  christos @ISA = qw(Exporter);
     19  1.1  christos @EXPORT = qw(cmp_versions quotify1 quotify_l fixup_cmd_elements fixup_cmd
     20  1.1  christos              dump_data);
     21  1.1  christos @EXPORT_OK = qw();
     22  1.1  christos 
     23  1.1  christos =head1 NAME
     24  1.1  christos 
     25  1.1  christos OpenSSL::Util - small OpenSSL utilities
     26  1.1  christos 
     27  1.1  christos =head1 SYNOPSIS
     28  1.1  christos 
     29  1.1  christos   use OpenSSL::Util;
     30  1.1  christos 
     31  1.1  christos   $versiondiff = cmp_versions('1.0.2k', '3.0.1');
     32  1.1  christos   # $versiondiff should be -1
     33  1.1  christos 
     34  1.1  christos   $versiondiff = cmp_versions('1.1.0', '1.0.2a');
     35  1.1  christos   # $versiondiff should be 1
     36  1.1  christos 
     37  1.1  christos   $versiondiff = cmp_versions('1.1.1', '1.1.1');
     38  1.1  christos   # $versiondiff should be 0
     39  1.1  christos 
     40  1.1  christos =head1 DESCRIPTION
     41  1.1  christos 
     42  1.1  christos =over
     43  1.1  christos 
     44  1.1  christos =item B<cmp_versions "VERSION1", "VERSION2">
     45  1.1  christos 
     46  1.1  christos Compares VERSION1 with VERSION2, paying attention to OpenSSL versioning.
     47  1.1  christos 
     48  1.1  christos Returns 1 if VERSION1 is greater than VERSION2, 0 if they are equal, and
     49  1.1  christos -1 if VERSION1 is less than VERSION2.
     50  1.1  christos 
     51  1.1  christos =back
     52  1.1  christos 
     53  1.1  christos =cut
     54  1.1  christos 
     55  1.1  christos # Until we're rid of everything with the old version scheme,
     56  1.1  christos # we need to be able to handle older style x.y.zl versions.
     57  1.1  christos # In terms of comparison, the x.y.zl and the x.y.z schemes
     58  1.1  christos # are compatible...  mostly because the latter starts at a
     59  1.1  christos # new major release with a new major number.
     60  1.1  christos sub _ossl_versionsplit {
     61  1.1  christos     my $textversion = shift;
     62  1.1  christos     return $textversion if $textversion eq '*';
     63  1.1  christos     my ($major,$minor,$edit,$letter) =
     64  1.1  christos         $textversion =~ /^(\d+)\.(\d+)\.(\d+)([a-z]{0,2})$/;
     65  1.1  christos 
     66  1.1  christos     return ($major,$minor,$edit,$letter);
     67  1.1  christos }
     68  1.1  christos 
     69  1.1  christos sub cmp_versions {
     70  1.1  christos     my @a_split = _ossl_versionsplit(shift);
     71  1.1  christos     my @b_split = _ossl_versionsplit(shift);
     72  1.1  christos     my $verdict = 0;
     73  1.1  christos 
     74  1.1  christos     while (@a_split) {
     75  1.1  christos         # The last part is a letter sequence (or a '*')
     76  1.1  christos         if (scalar @a_split == 1) {
     77  1.1  christos             $verdict = $a_split[0] cmp $b_split[0];
     78  1.1  christos         } else {
     79  1.1  christos             $verdict = $a_split[0] <=> $b_split[0];
     80  1.1  christos         }
     81  1.1  christos         shift @a_split;
     82  1.1  christos         shift @b_split;
     83  1.1  christos         last unless $verdict == 0;
     84  1.1  christos     }
     85  1.1  christos 
     86  1.1  christos     return $verdict;
     87  1.1  christos }
     88  1.1  christos 
     89  1.1  christos # It might be practical to quotify some strings and have them protected
     90  1.1  christos # from possible harm.  These functions primarily quote things that might
     91  1.1  christos # be interpreted wrongly by a perl eval.
     92  1.1  christos 
     93  1.1  christos =over 4
     94  1.1  christos 
     95  1.1  christos =item quotify1 STRING
     96  1.1  christos 
     97  1.1  christos This adds quotes (") around the given string, and escapes any $, @, \,
     98  1.1  christos " and ' by prepending a \ to them.
     99  1.1  christos 
    100  1.1  christos =back
    101  1.1  christos 
    102  1.1  christos =cut
    103  1.1  christos 
    104  1.1  christos sub quotify1 {
    105  1.1  christos     my $s = shift @_;
    106  1.1  christos     $s =~ s/([\$\@\\"'])/\\$1/g;
    107  1.1  christos     '"'.$s.'"';
    108  1.1  christos }
    109  1.1  christos 
    110  1.1  christos =over 4
    111  1.1  christos 
    112  1.1  christos =item quotify_l LIST
    113  1.1  christos 
    114  1.1  christos For each defined element in LIST (i.e. elements that aren't undef), have
    115  1.1  christos it quotified with 'quotify1'.
    116  1.1  christos Undefined elements are ignored.
    117  1.1  christos 
    118  1.1  christos =cut
    119  1.1  christos 
    120  1.1  christos sub quotify_l {
    121  1.1  christos     map {
    122  1.1  christos         if (!defined($_)) {
    123  1.1  christos             ();
    124  1.1  christos         } else {
    125  1.1  christos             quotify1($_);
    126  1.1  christos         }
    127  1.1  christos     } @_;
    128  1.1  christos }
    129  1.1  christos 
    130  1.1  christos =over 4
    131  1.1  christos 
    132  1.1  christos =item fixup_cmd_elements LIST
    133  1.1  christos 
    134  1.1  christos Fixes up the command line elements given by LIST in a platform specific
    135  1.1  christos manner.
    136  1.1  christos 
    137  1.1  christos The result of this function is a copy of LIST with strings where quotes and
    138  1.1  christos escapes have been injected as necessary depending on the content of each
    139  1.1  christos LIST string.
    140  1.1  christos 
    141  1.1  christos This can also be used to put quotes around the executable of a command.
    142  1.1  christos I<This must never ever be done on VMS.>
    143  1.1  christos 
    144  1.1  christos =back
    145  1.1  christos 
    146  1.1  christos =cut
    147  1.1  christos 
    148  1.1  christos sub fixup_cmd_elements {
    149  1.1  christos     # A formatter for the command arguments, defaulting to the Unix setup
    150  1.1  christos     my $arg_formatter =
    151  1.1  christos         sub { $_ = shift;
    152  1.1  christos               ($_ eq '' || /\s|[\{\}\\\$\[\]\*\?\|\&:;<>]/) ? "'$_'" : $_ };
    153  1.1  christos 
    154  1.1  christos     if ( $^O eq "VMS") {        # VMS setup
    155  1.1  christos         $arg_formatter = sub {
    156  1.1  christos             $_ = shift;
    157  1.1  christos             if ($_ eq '' || /\s|[!"[:upper:]]/) {
    158  1.1  christos                 s/"/""/g;
    159  1.1  christos                 '"'.$_.'"';
    160  1.1  christos             } else {
    161  1.1  christos                 $_;
    162  1.1  christos             }
    163  1.1  christos         };
    164  1.1  christos     } elsif ( $^O eq "MSWin32") { # MSWin setup
    165  1.1  christos         $arg_formatter = sub {
    166  1.1  christos             $_ = shift;
    167  1.1  christos             if ($_ eq '' || /\s|["\|\&\*\;<>]/) {
    168  1.1  christos                 s/(["\\])/\\$1/g;
    169  1.1  christos                 '"'.$_.'"';
    170  1.1  christos             } else {
    171  1.1  christos                 $_;
    172  1.1  christos             }
    173  1.1  christos         };
    174  1.1  christos     }
    175  1.1  christos 
    176  1.1  christos     return ( map { $arg_formatter->($_) } @_ );
    177  1.1  christos }
    178  1.1  christos 
    179  1.1  christos =over 4
    180  1.1  christos 
    181  1.1  christos =item fixup_cmd LIST
    182  1.1  christos 
    183  1.1  christos This is a sibling of fixup_cmd_elements() that expects the LIST to be a
    184  1.1  christos complete command line.  It does the same thing as fixup_cmd_elements(),
    185  1.1  christos expect that it treats the first LIST element specially on VMS.
    186  1.1  christos 
    187  1.1  christos =back
    188  1.1  christos 
    189  1.1  christos =cut
    190  1.1  christos 
    191  1.1  christos sub fixup_cmd {
    192  1.1  christos     return fixup_cmd_elements(@_) unless $^O eq 'VMS';
    193  1.1  christos 
    194  1.1  christos     # The rest is VMS specific
    195  1.1  christos     my $cmd = shift;
    196  1.1  christos 
    197  1.1  christos     # Prefix to be applied as needed.  Essentially, we need to determine
    198  1.1  christos     # if the command is an executable file (something.EXE), and invoke it
    199  1.1  christos     # with the MCR command in that case.  MCR is an old PDP-11 command that
    200  1.1  christos     # stuck around.
    201  1.1  christos     my @prefix;
    202  1.1  christos 
    203  1.1  christos     if ($cmd =~ m|^\@|) {
    204  1.1  christos         # The command is an invocation of a command procedure (also known as
    205  1.1  christos         # "script"), no modification needed.
    206  1.1  christos         @prefix = ();
    207  1.1  christos     } elsif ($cmd =~ m|^MCR$|) {
    208  1.1  christos         # The command is MCR, so there's nothing much to do apart from
    209  1.1  christos         # making sure that the file name following it isn't treated with
    210  1.1  christos         # fixup_cmd_elements(), 'cause MCR doesn't like strings.
    211  1.1  christos         @prefix = ( $cmd );
    212  1.1  christos         $cmd = shift;
    213  1.1  christos     } else {
    214  1.1  christos         # All that's left now is to check whether the command is an executable
    215  1.1  christos         # file, and if it's not, simply assume that it is a DCL command.
    216  1.1  christos 
    217  1.1  christos         # Make sure we have a proper file name, i.e. add the default
    218  1.1  christos         # extension '.exe' if there isn't one already.
    219  1.1  christos         my $executable = ($cmd =~ m|.[a-z0-9\$]*$|) ? $cmd : $cmd . '.exe';
    220  1.1  christos         if (-e $executable) {
    221  1.1  christos             # It seems to be an executable, so we make sure to prefix it
    222  1.1  christos             # with MCR, for proper invocation.  We also make sure that
    223  1.1  christos             # there's a directory specification, or otherwise, MCR will
    224  1.1  christos             # assume that the executable is in SYS$SYSTEM:
    225  1.1  christos             @prefix = ( 'MCR' );
    226  1.1  christos             $cmd = '[]' . $cmd unless $cmd =~ /^(?:[\$a-z0-9_]+:)?[<\[]/i;
    227  1.1  christos         } else {
    228  1.1  christos             # If it isn't an executable, then we assume that it's a DCL
    229  1.1  christos             # command, and do no further processing, apart from argument
    230  1.1  christos             # fixup.
    231  1.1  christos             @prefix = ();
    232  1.1  christos         }
    233  1.1  christos     }
    234  1.1  christos 
    235  1.1  christos     return ( @prefix, $cmd, fixup_cmd_elements(@_) );
    236  1.1  christos }
    237  1.1  christos 
    238  1.1  christos =item dump_data REF, OPTS
    239  1.1  christos 
    240  1.1  christos Dump the data from REF into a string that can be evaluated into the same
    241  1.1  christos data by Perl.
    242  1.1  christos 
    243  1.1  christos OPTS is the rest of the arguments, expected to be pairs formed with C<< => >>.
    244  1.1  christos The following OPTS keywords are understood:
    245  1.1  christos 
    246  1.1  christos =over 4
    247  1.1  christos 
    248  1.1  christos =item B<delimiters =E<gt> 0 | 1>
    249  1.1  christos 
    250  1.1  christos Include the outer delimiter of the REF type in the resulting string if C<1>,
    251  1.1  christos otherwise not.
    252  1.1  christos 
    253  1.1  christos =item B<indent =E<gt> num>
    254  1.1  christos 
    255  1.1  christos The indentation of the caller, i.e. an initial value.  If not given, there
    256  1.1  christos will be no indentation at all, and the string will only be one line.
    257  1.1  christos 
    258  1.1  christos =back
    259  1.1  christos 
    260  1.1  christos =cut
    261  1.1  christos 
    262  1.1  christos sub dump_data {
    263  1.1  christos     my $ref = shift;
    264  1.1  christos     # Available options:
    265  1.1  christos     # indent           => callers indentation ( undef for no indentation,
    266  1.1  christos     #                     an integer otherwise )
    267  1.1  christos     # delimiters       => 1 if outer delimiters should be added
    268  1.1  christos     my %opts = @_;
    269  1.1  christos 
    270  1.1  christos     my $indent = $opts{indent} // 1;
    271  1.1  christos     # Indentation of the whole structure, where applicable
    272  1.1  christos     my $nlindent1 = defined $opts{indent} ? "\n" . ' ' x $indent : ' ';
    273  1.1  christos     # Indentation of individual items, where applicable
    274  1.1  christos     my $nlindent2 = defined $opts{indent} ? "\n" . ' ' x ($indent + 4) : ' ';
    275  1.1  christos     my %subopts = ();
    276  1.1  christos 
    277  1.1  christos     $subopts{delimiters} = 1;
    278  1.1  christos     $subopts{indent} = $opts{indent} + 4 if defined $opts{indent};
    279  1.1  christos 
    280  1.1  christos     my $product;      # Finished product, or reference to a function that
    281  1.1  christos                       # produces a string, given $_
    282  1.1  christos     # The following are only used when $product is a function reference
    283  1.1  christos     my $delim_l;      # Left delimiter of structure
    284  1.1  christos     my $delim_r;      # Right delimiter of structure
    285  1.1  christos     my $separator;    # Item separator
    286  1.1  christos     my @items;        # Items to iterate over
    287  1.1  christos 
    288  1.1  christos      if (ref($ref) eq "ARRAY") {
    289  1.1  christos          if (scalar @$ref == 0) {
    290  1.1  christos              $product = $opts{delimiters} ? '[]' : '';
    291  1.1  christos          } else {
    292  1.1  christos              $product = sub {
    293  1.1  christos                  dump_data(\$_, %subopts)
    294  1.1  christos              };
    295  1.1  christos              $delim_l = ($opts{delimiters} ? '[' : '').$nlindent2;
    296  1.1  christos              $delim_r = $nlindent1.($opts{delimiters} ? ']' : '');
    297  1.1  christos              $separator = ",$nlindent2";
    298  1.1  christos              @items = @$ref;
    299  1.1  christos          }
    300  1.1  christos      } elsif (ref($ref) eq "HASH") {
    301  1.1  christos          if (scalar keys %$ref == 0) {
    302  1.1  christos              $product = $opts{delimiters} ? '{}' : '';
    303  1.1  christos          } else {
    304  1.1  christos              $product = sub {
    305  1.1  christos                  quotify1($_) . " => " . dump_data($ref->{$_}, %subopts);
    306  1.1  christos              };
    307  1.1  christos              $delim_l = ($opts{delimiters} ? '{' : '').$nlindent2;
    308  1.1  christos              $delim_r = $nlindent1.($opts{delimiters} ? '}' : '');
    309  1.1  christos              $separator = ",$nlindent2";
    310  1.1  christos              @items = sort keys %$ref;
    311  1.1  christos          }
    312  1.1  christos      } elsif (ref($ref) eq "SCALAR") {
    313  1.1  christos          $product = defined $$ref ? quotify1 $$ref : "undef";
    314  1.1  christos      } else {
    315  1.1  christos          $product = defined $ref ? quotify1 $ref : "undef";
    316  1.1  christos      }
    317  1.1  christos 
    318  1.1  christos      if (ref($product) eq "CODE") {
    319  1.1  christos          $delim_l . join($separator, map { &$product } @items) . $delim_r;
    320  1.1  christos      } else {
    321  1.1  christos          $product;
    322  1.1  christos      }
    323  1.1  christos }
    324  1.1  christos 
    325  1.1  christos =back
    326  1.1  christos 
    327  1.1  christos =cut
    328  1.1  christos 
    329  1.1  christos 1;
    330