package Amon2::Util;
use strict;
use warnings;
use base qw/Exporter/;
use File::Spec;
use MIME::Base64 ();
use Digest::SHA ();
use Time::HiRes;
use POSIX ();
use Carp ();
our @EXPORT_OK = qw/add_method random_string/;
sub add_method {
my ($klass, $method, $code) = @_;
no strict 'refs';
*{"${klass}::${method}"} = $code;
}
sub base_dir($) {
my $path = shift;
$path =~ s!::!/!g;
if (my $libpath = $INC{"$path.pm"}) {
$libpath =~ s!\\!/!g; # win32
$libpath =~ s!(?:blib/)?lib/+$path\.pm$!!;
File::Spec->rel2abs($libpath || './');
} else {
File::Spec->rel2abs('./');
}
}
our $URANDOM_FH;
# $URANDOM_FH is undef if there is no /dev/urandom
open $URANDOM_FH, '<:raw', '/dev/urandom'
or do {
undef($URANDOM_FH);
warn "Cannot open /dev/urandom: $!.";
};
sub random_string {
my $len = shift;
# 27 is the sha1_base64() length.
if ($len < 27) {
Carp::cluck("Amon2::Util::random_string: Length too short. You should use 27+ bytes for security reason.");
}
if ($URANDOM_FH) {
my $src_len = POSIX::ceil($len/3.0*4.0);
# Generate session id from /dev/urandom.
my $read = read($URANDOM_FH, my $buf, $src_len);
if ($read != $src_len) {
die "Cannot read bytes from /dev/urandom: $!";
}
my $result = MIME::Base64::encode_base64($buf, '');
$result =~ tr|+/=|\-_|d; # make it url safe
return substr($result, 0, $len);
} else {
# It's weaker than above. But it's portable.
my $out = '';
while (length($out) < $len) {
my $sha1 = Digest::SHA::sha1_base64(rand() . $$ . {} . Time::HiRes::time());
$sha1 =~ tr|+/=|\-_|d; # make it url safe
$out .= $sha1;
}
return substr($out, 0, $len);
}
}
1;
__END__
=head1 DESCRIPTION
This is a utility class for Amon2. Do not use this directly.