Home | History | Annotate | Line # | Download | only in OpenSSL
      1  1.1  christos #! /usr/bin/env perl
      2  1.1  christos # Copyright 2018-2021 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 $prog = shift;
    196  1.1  christos 
    197  1.1  christos     # On VMS, running random executables without having a command symbol
    198  1.1  christos     # means running them with the MCR command.  This is an old PDP-11
    199  1.1  christos     # command that stuck around.
    200  1.1  christos     # This assumes that we're passed the name of an executable.  This is a
    201  1.1  christos     # safe assumption for OpenSSL command lines
    202  1.1  christos     my $prefix = 'MCR';
    203  1.1  christos 
    204  1.1  christos     if ($prog =~ /^MCR$/i) {
    205  1.1  christos         # If the first element is "MCR" (independent of case) already, then
    206  1.1  christos         # we assume that the program it runs is already written the way it
    207  1.1  christos         # should, and just grab it.
    208  1.1  christos         $prog = shift;
    209  1.1  christos     } else {
    210  1.1  christos         # If the command itself doesn't have a directory spec, make sure
    211  1.1  christos         # that there is one.  Otherwise, MCR assumes that the program
    212  1.1  christos         # resides in SYS$SYSTEM:
    213  1.1  christos         $prog = '[]' . $prog unless $prog =~ /^(?:[\$a-z0-9_]+:)?[<\[]/i;
    214  1.1  christos     }
    215  1.1  christos 
    216  1.1  christos     return ( $prefix, $prog, fixup_cmd_elements(@_) );
    217  1.1  christos }
    218  1.1  christos 
    219  1.1  christos =item dump_data REF, OPTS
    220  1.1  christos 
    221  1.1  christos Dump the data from REF into a string that can be evaluated into the same
    222  1.1  christos data by Perl.
    223  1.1  christos 
    224  1.1  christos OPTS is the rest of the arguments, expected to be pairs formed with C<< => >>.
    225  1.1  christos The following OPTS keywords are understood:
    226  1.1  christos 
    227  1.1  christos =over 4
    228  1.1  christos 
    229  1.1  christos =item B<delimiters =E<gt> 0 | 1>
    230  1.1  christos 
    231  1.1  christos Include the outer delimiter of the REF type in the resulting string if C<1>,
    232  1.1  christos otherwise not.
    233  1.1  christos 
    234  1.1  christos =item B<indent =E<gt> num>
    235  1.1  christos 
    236  1.1  christos The indentation of the caller, i.e. an initial value.  If not given, there
    237  1.1  christos will be no indentation at all, and the string will only be one line.
    238  1.1  christos 
    239  1.1  christos =back
    240  1.1  christos 
    241  1.1  christos =cut
    242  1.1  christos 
    243  1.1  christos sub dump_data {
    244  1.1  christos     my $ref = shift;
    245  1.1  christos     # Available options:
    246  1.1  christos     # indent           => callers indentation ( undef for no indentation,
    247  1.1  christos     #                     an integer otherwise )
    248  1.1  christos     # delimiters       => 1 if outer delimiters should be added
    249  1.1  christos     my %opts = @_;
    250  1.1  christos 
    251  1.1  christos     my $indent = $opts{indent} // 1;
    252  1.1  christos     # Indentation of the whole structure, where applicable
    253  1.1  christos     my $nlindent1 = defined $opts{indent} ? "\n" . ' ' x $indent : ' ';
    254  1.1  christos     # Indentation of individual items, where applicable
    255  1.1  christos     my $nlindent2 = defined $opts{indent} ? "\n" . ' ' x ($indent + 4) : ' ';
    256  1.1  christos     my %subopts = ();
    257  1.1  christos 
    258  1.1  christos     $subopts{delimiters} = 1;
    259  1.1  christos     $subopts{indent} = $opts{indent} + 4 if defined $opts{indent};
    260  1.1  christos 
    261  1.1  christos     my $product;      # Finished product, or reference to a function that
    262  1.1  christos                       # produces a string, given $_
    263  1.1  christos     # The following are only used when $product is a function reference
    264  1.1  christos     my $delim_l;      # Left delimiter of structure
    265  1.1  christos     my $delim_r;      # Right delimiter of structure
    266  1.1  christos     my $separator;    # Item separator
    267  1.1  christos     my @items;        # Items to iterate over
    268  1.1  christos 
    269  1.1  christos      if (ref($ref) eq "ARRAY") {
    270  1.1  christos          if (scalar @$ref == 0) {
    271  1.1  christos              $product = $opts{delimiters} ? '[]' : '';
    272  1.1  christos          } else {
    273  1.1  christos              $product = sub {
    274  1.1  christos                  dump_data(\$_, %subopts)
    275  1.1  christos              };
    276  1.1  christos              $delim_l = ($opts{delimiters} ? '[' : '').$nlindent2;
    277  1.1  christos              $delim_r = $nlindent1.($opts{delimiters} ? ']' : '');
    278  1.1  christos              $separator = ",$nlindent2";
    279  1.1  christos              @items = @$ref;
    280  1.1  christos          }
    281  1.1  christos      } elsif (ref($ref) eq "HASH") {
    282  1.1  christos          if (scalar keys %$ref == 0) {
    283  1.1  christos              $product = $opts{delimiters} ? '{}' : '';
    284  1.1  christos          } else {
    285  1.1  christos              $product = sub {
    286  1.1  christos                  quotify1($_) . " => " . dump_data($ref->{$_}, %subopts);
    287  1.1  christos              };
    288  1.1  christos              $delim_l = ($opts{delimiters} ? '{' : '').$nlindent2;
    289  1.1  christos              $delim_r = $nlindent1.($opts{delimiters} ? '}' : '');
    290  1.1  christos              $separator = ",$nlindent2";
    291  1.1  christos              @items = sort keys %$ref;
    292  1.1  christos          }
    293  1.1  christos      } elsif (ref($ref) eq "SCALAR") {
    294  1.1  christos          $product = defined $$ref ? quotify1 $$ref : "undef";
    295  1.1  christos      } else {
    296  1.1  christos          $product = defined $ref ? quotify1 $ref : "undef";
    297  1.1  christos      }
    298  1.1  christos 
    299  1.1  christos      if (ref($product) eq "CODE") {
    300  1.1  christos          $delim_l . join($separator, map { &$product } @items) . $delim_r;
    301  1.1  christos      } else {
    302  1.1  christos          $product;
    303  1.1  christos      }
    304  1.1  christos }
    305  1.1  christos 
    306  1.1  christos =back
    307  1.1  christos 
    308  1.1  christos =cut
    309  1.1  christos 
    310  1.1  christos 1;
    311