#!perl
$Net::Azure::StorageClient::VERSION
=
'0.6'
;
sub
new {
my
$class
=
shift
;
my
%args
=
@_
;
my
$type
=
$args
{ type };
if
(
$type
&&
$type
=~ m/^Blob$/i ) {
$type
=
ucfirst
(
$type
);
$class
.=
'::'
.
$type
;
eval
"require $class;"
;
}
my
$obj
=
bless
{},
$class
;
$obj
->{ type } =
lc
(
$type
)
if
$type
;
$obj
->init(
@_
);
}
sub
init {
my
(
$self
,
%args
) =
@_
;
$self
->{ account_name } =
$args
{ account_name };
$self
->{ primary_access_key } =
$args
{ primary_access_key };
$self
->{ api_version } =
$args
{ api_version } ||
'2012-02-12'
;
$self
->{ protocol } =
$args
{ protocol } ||
'https'
;
return
$self
;
}
sub
sign {
my
(
$self
,
$req
,
$params
) =
@_
;
my
$key
=
$self
->{ primary_access_key };
my
$api_version
=
$self
->{ api_version };
$req
->header(
'x-ms-version'
,
$api_version
);
$req
->header(
'x-ms-date'
, time2str() );
if
(
my
$data
=
$params
->{ body } ) {
$req
->header(
'Content-MD5'
, md5_base64(
$data
) .
'=='
);
}
if
(
$params
&& (
my
$headers
=
$params
->{ headers } ) ) {
for
my
$key
(
keys
%$headers
) {
$req
->header(
$key
,
$headers
->{
$key
} );
}
}
my
$canonicalized_headers
=
join
''
,
map
{
lc
(
$_
) .
':'
.
$req
->header(
$_
) .
"\n"
}
sort
grep
{ /^x-ms/ }
keys
%{
$req
->headers };
my
$account
=
$req
->uri->authority;
$account
=~ s/^(\w+).*$/$1/;
my
$path
=
$req
->uri->path;
my
$canonicalized_resource
=
"/${account}${path}"
;
$canonicalized_resource
.=
join
''
,
map
{
"\n"
.
lc
(
$_
) .
':'
.
join
(
','
,
sort
$req
->uri->query_param(
$_
) ) }
sort
$req
->uri->query_param;
my
$method
=
$req
->method;
my
$encoding
=
$req
->header(
'Content-Encoding'
) ||
''
;
my
$language
=
$req
->header(
'Content-Language'
) ||
''
;
my
$length
=
$req
->header(
'Content-Length'
);
if
(!
defined
$length
) {
$length
=
''
;
}
my
$md5
=
$req
->header(
'Content-MD5'
) ||
''
;
my
$content_type
=
$req
->header(
'Content-Type'
) ||
''
;
my
$date
=
$req
->header(
'Date'
) ||
''
;
my
$if_mod_since
=
$req
->header(
'If-Modified-Since'
) ||
''
;
my
$if_match
=
$req
->header(
'If-Match'
) ||
''
;
my
$if_none_match
=
$req
->header(
'If-None-Match'
) ||
''
;
my
$if_unmod_since
=
$req
->header(
'If-Unmodified-Since'
) ||
''
;
my
$range
=
$req
->header(
'Range'
) ||
''
;
my
@headers
= (
$method
,
$encoding
,
$language
,
$length
,
$md5
,
$content_type
,
$date
,
$if_mod_since
,
$if_match
,
$if_none_match
,
$if_unmod_since
,
$range
);
push
(
@headers
,
"${canonicalized_headers}${canonicalized_resource}"
);
my
$string_to_sign
=
join
(
"\n"
,
@headers
);
my
$signature
= hmac_sha256_base64(
$string_to_sign
, decode_base64(
$key
) );
$signature
.=
'='
x ( 4 - (
length
(
$signature
) % 4 ) );
$req
->authorization(
"SharedKey ${account}:${signature}"
);
return
$req
;
}
{
my
$ua
= LWP::UserAgent->new;
sub
request {
my
(
$self
,
$method
,
$url
,
$params
) =
@_
;
$url
=
''
unless
(
$url
);
if
(
$url
!~ m!^https{0,1}://! ) {
if
(
$url
!~ m !^/! ) {
$url
=
'/'
.
$url
;
}
my
$type
=
$self
->{ type };
my
$account
=
$self
->{ account_name };
my
$protocol
=
$self
->{ protocol };
$url
=
"${protocol}://${account}.${type}.core.windows.net${url}"
;
}
my
$body
;
if
(
defined
(
$params
->{ body } ) ) {
$body
=
$params
->{ body };
}
$method
=
'GET'
unless
$method
;
my
$req
= HTTP::Request->new(
$method
=>
$url
);
$req
->content_length(
length
(
$body
) )
if
defined
$body
;
$req
=
$self
->sign(
$req
,
$params
);
$req
->content(
$body
)
if
defined
$body
;
return
$ua
->request(
$req
);
}
}
sub
get {
my
$self
=
shift
;
$self
->request(
'GET'
,
@_
);
}
sub
head {
my
$self
=
shift
;
$self
->request(
'HEAD'
,
@_
);
}
sub
put {
my
$self
=
shift
;
$self
->request(
'PUT'
,
@_
);
}
sub
delete
{
my
$self
=
shift
;
$self
->request(
'DELETE'
,
@_
);
}
sub
post {
my
$self
=
shift
;
$self
->request(
'POST'
,
@_
);
}
sub
_signed_identifier {
my
(
$self
,
$length
) =
@_
;
my
@char
= () ;
push
@char
, (
'a'
..
'z'
);
push
@char
, (
'A'
..
'Z'
);
push
@char
, ( 0 .. 9 );
my
$res
=
''
;
for
(
my
$i
=1;
$i
<=
$length
;
$i
++ ) {
$res
.=
$char
[
int
(
rand
(
$#char
+ 1 ) ) ];
}
return
$res
;
}
sub
_adjust_path {
my
(
$self
,
$path
) =
@_
;
$path
=~ s!^/!!;
if
(
my
$type
=
$self
->{ type } ) {
my
$arg
;
if
(
$type
eq
'blob'
) {
$arg
=
'container_name'
}
if
(
$arg
&& (
my
$root
=
$self
->{
$arg
} ) ) {
$path
=
$root
.
'/'
.
$path
;
}
}
return
$path
;
}
1;