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