line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Amazon::S3::SignedURLGenerator; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
46811
|
use strict; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
26
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
34
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
46
|
|
8
|
1
|
|
|
1
|
|
263
|
use URI::Escape; |
|
1
|
|
|
|
|
1135
|
|
|
1
|
|
|
|
|
48
|
|
9
|
1
|
|
|
1
|
|
246
|
use Digest::HMAC_SHA1; |
|
1
|
|
|
|
|
3411
|
|
|
1
|
|
|
|
|
36
|
|
10
|
1
|
|
|
1
|
|
244
|
use MIME::Base64 qw(encode_base64); |
|
1
|
|
|
|
|
446
|
|
|
1
|
|
|
|
|
723
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub new { |
13
|
0
|
|
|
0
|
0
|
|
my $proto = shift; |
14
|
0
|
|
0
|
|
|
|
my $class = ref($proto) || $proto; |
15
|
|
|
|
|
|
|
|
16
|
0
|
0
|
|
|
|
|
my %args = scalar(@_) % 2 ? %{$_[0]} : @_; |
|
0
|
|
|
|
|
|
|
17
|
0
|
0
|
|
|
|
|
$args{aws_access_key_id} or croak 'aws_access_key_id is required'; |
18
|
0
|
0
|
|
|
|
|
$args{aws_secret_access_key} or croak 'aws_secret_access_key is required'; |
19
|
|
|
|
|
|
|
|
20
|
0
|
|
0
|
|
|
|
$args{prefix} ||= 'https://s3.amazonaws.com'; |
21
|
0
|
|
0
|
|
|
|
$args{expires} ||= 3600; |
22
|
|
|
|
|
|
|
|
23
|
0
|
|
|
|
|
|
$args{prefix} =~ s/\/$//; # remove last / |
24
|
|
|
|
|
|
|
|
25
|
0
|
|
|
|
|
|
return bless \%args, $class; |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub generate_url { |
29
|
0
|
|
|
0
|
0
|
|
my ($self, $method, $path, $headers) = @_; |
30
|
|
|
|
|
|
|
|
31
|
0
|
|
|
|
|
|
$path =~ s/^\///; |
32
|
0
|
|
0
|
|
|
|
$headers ||= {}; |
33
|
0
|
|
0
|
|
|
|
my $expires = $headers->{expires} || (time() + $self->{expires}); |
34
|
|
|
|
|
|
|
|
35
|
0
|
|
|
|
|
|
my $x_path = $path; |
36
|
0
|
0
|
|
|
|
|
if ($self->{prefix} =~ '//(.*)\.s3') { |
37
|
0
|
|
|
|
|
|
$x_path = $1 . '/' . $path; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
0
|
|
|
|
|
|
my $canonical_string = __canonical_string($method, $x_path, $headers, $expires); |
41
|
0
|
|
|
|
|
|
my $encoded_canonical = __encode($self->{aws_secret_access_key}, $canonical_string, 1); |
42
|
0
|
0
|
|
|
|
|
if (index($path, '?') == -1) { |
43
|
0
|
|
|
|
|
|
return "$self->{prefix}/$path?Signature=$encoded_canonical&Expires=$expires&AWSAccessKeyId=$self->{aws_access_key_id}"; |
44
|
|
|
|
|
|
|
} else { |
45
|
0
|
|
|
|
|
|
return "$self->{prefix}/$path&Signature=$encoded_canonical&Expires=$expires&AWSAccessKeyId=$self->{aws_access_key_id}"; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
our $AMAZON_HEADER_PREFIX = 'x-amz-'; |
50
|
|
|
|
|
|
|
our $METADATA_PREFIX = 'x-amz-meta-'; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub __trim { |
53
|
0
|
|
|
0
|
|
|
my ($value) = @_; |
54
|
|
|
|
|
|
|
|
55
|
0
|
|
|
|
|
|
$value =~ s/^\s+//; |
56
|
0
|
|
|
|
|
|
$value =~ s/\s+$//; |
57
|
0
|
|
|
|
|
|
return $value; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# generate a canonical string for the given parameters. expires is optional and is |
61
|
|
|
|
|
|
|
# only used by query string authentication. |
62
|
|
|
|
|
|
|
sub __canonical_string { |
63
|
0
|
|
|
0
|
|
|
my ($method, $path, $headers, $expires) = @_; |
64
|
0
|
|
|
|
|
|
my %interesting_headers = (); |
65
|
0
|
|
|
|
|
|
while (my ($key, $value) = each %$headers) { |
66
|
0
|
|
|
|
|
|
my $lk = lc $key; |
67
|
0
|
0
|
0
|
|
|
|
if ( |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
68
|
|
|
|
|
|
|
$lk eq 'content-md5' or |
69
|
|
|
|
|
|
|
$lk eq 'content-type' or |
70
|
|
|
|
|
|
|
$lk eq 'date' or |
71
|
|
|
|
|
|
|
$lk =~ /^$AMAZON_HEADER_PREFIX/ |
72
|
|
|
|
|
|
|
) { |
73
|
0
|
|
|
|
|
|
$interesting_headers{$lk} = __trim($value); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# these keys get empty strings if they don't exist |
78
|
0
|
|
0
|
|
|
|
$interesting_headers{'content-type'} ||= ''; |
79
|
0
|
|
0
|
|
|
|
$interesting_headers{'content-md5'} ||= ''; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# just in case someone used this. it's not necessary in this lib. |
82
|
0
|
0
|
|
|
|
|
$interesting_headers{'date'} = '' if $interesting_headers{'x-amz-date'}; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# if you're using expires for query string auth, then it trumps date |
85
|
|
|
|
|
|
|
# (and x-amz-date) |
86
|
0
|
0
|
|
|
|
|
$interesting_headers{'date'} = $expires if $expires; |
87
|
|
|
|
|
|
|
|
88
|
0
|
|
|
|
|
|
my $buf = "$method\n"; |
89
|
0
|
|
|
|
|
|
foreach my $key (sort keys %interesting_headers) { |
90
|
0
|
0
|
|
|
|
|
if ($key =~ /^$AMAZON_HEADER_PREFIX/) { |
91
|
0
|
|
|
|
|
|
$buf .= "$key:$interesting_headers{$key}\n"; |
92
|
|
|
|
|
|
|
} else { |
93
|
0
|
|
|
|
|
|
$buf .= "$interesting_headers{$key}\n"; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# don't include anything after the first ? in the resource... |
98
|
0
|
|
|
|
|
|
$path =~ /^([^?]*)/; |
99
|
0
|
|
|
|
|
|
$buf .= "/$1"; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# ...unless there is an acl or torrent parameter |
102
|
0
|
0
|
|
|
|
|
if ($path =~ /[&?]acl($|=|&)/) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
103
|
0
|
|
|
|
|
|
$buf .= '?acl'; |
104
|
|
|
|
|
|
|
} elsif ($path =~ /[&?]torrent($|=|&)/) { |
105
|
0
|
|
|
|
|
|
$buf .= '?torrent'; |
106
|
|
|
|
|
|
|
} elsif ($path =~ /[&?]logging($|=|&)/) { |
107
|
0
|
|
|
|
|
|
$buf .= '?logging'; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
|
|
|
return $buf; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# finds the hmac-sha1 hash of the canonical string and the aws secret access key and then |
114
|
|
|
|
|
|
|
# base64 encodes the result (optionally urlencoding after that). |
115
|
|
|
|
|
|
|
sub __encode { |
116
|
0
|
|
|
0
|
|
|
my ($aws_secret_access_key, $str, $urlencode) = @_; |
117
|
0
|
|
|
|
|
|
my $hmac = Digest::HMAC_SHA1->new($aws_secret_access_key); |
118
|
0
|
|
|
|
|
|
$hmac->add($str); |
119
|
0
|
|
|
|
|
|
my $b64 = encode_base64($hmac->digest, ''); |
120
|
0
|
0
|
|
|
|
|
if ($urlencode) { |
121
|
0
|
|
|
|
|
|
return __urlencode($b64); |
122
|
|
|
|
|
|
|
} else { |
123
|
0
|
|
|
|
|
|
return $b64; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub __urlencode { |
128
|
0
|
|
|
0
|
|
|
my ($unencoded) = @_; |
129
|
0
|
|
|
|
|
|
return uri_escape($unencoded, '^A-Za-z0-9_-'); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
1; |
133
|
|
|
|
|
|
|
__END__ |