Server IP : 195.201.23.43 / Your IP : 18.222.97.243 Web Server : Apache System : Linux webserver2.vercom.be 5.4.0-192-generic #212-Ubuntu SMP Fri Jul 5 09:47:39 UTC 2024 x86_64 User : kdecoratie ( 1041) PHP Version : 7.1.33-63+ubuntu20.04.1+deb.sury.org+1 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /usr/share/webmin/virtual-server/ |
Upload File : |
# Functions for accessing the Rackspace cloud files API # rs_connect(url, user, key) # Connect to rackspace and get an authentication token. Returns a hash ref for # a connection handle on success, or an error message on failure. sub rs_connect { my ($url, $user, $key) = @_; if ($url =~ /^(.*);(\S+)$/) { $url = $1; $region = lc($2); } my ($host, $port, $page, $ssl) = &parse_http_url($url); $host || return "Invalid URL : $url"; my $apiver; if ($url =~ /(v[0-9\.]+)$/) { $apiver = $1; } else { return "URL does not end with an API version : $url"; } my $headers = { 'X-Auth-User' => $user, 'X-Auth-Key' => $key }; my ($ok, $out, $rs_headers) = &rs_http_call($url, "GET", $headers); return "Authentication at $url failed : $out" if (!$ok); if (!$rs_headers->{'X-Auth-Token'}) { return "No authentication token received : $out"; } my $h = { 'url' => $url, 'storage-url' => $rs_headers->{'X-Storage-Url'}, 'cdn-url' => $rs_headers->{'X-Cdn-Management-Url'}, 'token' => $rs_headers->{'X-Auth-Token'}, 'user' => $user, 'key' => $key, 'api' => $apiver, }; if ($config{'rs_snet'}) { # Storage URL needs to be customized for the internal network $h->{'storage-url'} =~ s/^(http|https):\/\//$1:\/\/snet-/; } if ($region) { # Force to a specific region $h->{'storage-url'} =~ s/\.([a-z]+)(\d+)\.clouddrive/\.$region$2.clouddrive/; } return $h; } # rs_list_containers(&handle) # Returns a list of containers owned by the user, as an array ref. On error # returns the error message string sub rs_list_containers { my ($h) = @_; my ($ok, $out, $headers) = &rs_api_call($h, "", "GET"); return $out if (!$ok); return [ split(/\r?\n/, $out) ]; } # rs_create_container(&handle, container) # Creates a container with the given name. Returns undef on success, or an # error message on failure. sub rs_create_container { my ($h, $container) = @_; my ($ok, $out, $headers) = &rs_api_call($h, "/$container", "PUT"); return $ok ? undef : $out; } # rs_stat_container(&handle, container) # Returns a hash ref with metadata information about some container, or an error # message on failure. Available keys are like : # X-Container-Object-Count: 7 # X-Container-Bytes-Used: 413 # X-Container-Meta-InspectedBy: JackWolf sub rs_stat_container { my ($h, $container) = @_; my ($ok, $out, $headers) = &rs_api_call($h, "/$container", "HEAD"); return $out if (!$ok); return $headers; } # rs_delete_container(&handle, container, [recursive]) # Deletes some container from rackspace. Returns an error message on failure, or # undef on success sub rs_delete_container { my ($h, $container, $recursive) = @_; if ($recursive) { my $files = &rs_list_objects($h, $container); return $files if (!ref($files)); foreach my $f (@$files) { my $err = &rs_delete_object($h, $container, $f); return "$f : $err" if ($err); } } my ($ok, $out, $headers) = &rs_api_call($h, "/$container", "DELETE"); return $ok ? undef : $out; } # rs_list_objects(&handle, container) # Returns a list of object filenames in some container, as an array ref. On # error returns the error message string sub rs_list_objects { my ($h, $container) = @_; my $chunk = 10000; my @all; my $marker; while(1) { my ($ok, $out, $headers) = &rs_api_call($h, "/$container?limit=$chunk". ($marker eq '' ? '' : "&marker=$marker"), "GET"); return $out if (!$ok); my @part = split(/\r?\n/, $out); last if (!@part); # No more push(@all, @part); last if (@part < $chunk); # Got less than the chunk size $marker = $part[$#part]; } return \@all; } # rs_upload_object(&handle, container, file, source-file, [multipart], # [force-chunk-size]) # Uploads the contents of some local file to rackspace. Returns undef on success # or an error message string if something goes wrong sub rs_upload_object { my ($h, $container, $file, $src, $multipart, $chunk) = @_; my $def_rs_chunk_size = $config{'rs_chunk'}*1024*1024 || 200*1024*1024; # 200 MB $chunk ||= $def_rs_chunk_size; my @st = stat($src); @st || return "File $src does not exist"; if ($st[7] >= 2*1024*1024*1024 || $multipart) { # Large files have to be uploaded in parts my $pos = 0; my $pinheaders = { 'X-Object-Meta-PIN' => int(rand()*10000) }; my $n = "00000000"; while($pos < $st[7]) { # Upload a chunk my $want = $st[7] - $pos; if ($want > $chunk) { $want = $chunk; } my ($ok, $out); for(my $try=0; $try<3; $try++) { ($ok, $out) = &rs_api_call($h, "/$container/$file.$n", "PUT", $pinheaders, undef, $src, $pos, $want); last if ($ok); } if (!$ok) { close(CHUNK); return "Upload failed at $pos : $out"; } $pos += $want; $n++; } close(CHUNK); # Finally upload the manifest $pinheaders->{'X-Object-Manifest'} = "$container/$file"; my ($ok, $out) = &rs_api_call($h, "/$container/$file", "PUT", $pinheaders, undef, ""); return $ok ? undef : "Manifest upload filed : $out"; } else { # Can upload in a single API call my ($ok, $out) = &rs_api_call($h, "/$container/$file", "PUT", undef, undef, $src); return $ok ? undef : $out; } } # rs_download_object(&handle, container, file, dest-file) # Downloads the contents of rackspace file to local. Returns undef on success # or an error message string if something goes wrong sub rs_download_object { my ($h, $container, $file, $dst) = @_; my ($ok, $out) = &rs_api_call($h, "/$container/$file", "GET", undef, $dst); return $ok ? undef : $out; } # rs_stat_object(&handle, container, file) # Returns a hash ref with metadata information about some object, or an error # message on failure. Available keys are like : # Last-Modified: Fri, 12 Jun 2007 13:40:18 GMT # ETag: 8a964ee2a5e88be344f36c22562a6486 # Content-Length: 512000 # Content-Type: text/plain; charset=UTF-8 # X-Object-Meta-Meat: Bacon sub rs_stat_object { my ($h, $container, $file) = @_; my ($ok, $out, $headers) = &rs_api_call($h, "/$container/$file", "HEAD"); return $out if (!$ok); return $headers; } # rs_delete_object(&handle, container, file) # Deletes some file from a container. Returns an error message on failure, or # undef on success sub rs_delete_object { my ($h, $container, $file) = @_; my $st = &rs_stat_object($h, $container, $file); if (ref($st) && $st->{'X-Object-Manifest'}) { # Looks multi-part .. delete the parts first my ($mcontainer, $mprefix) = split(/\//, $st->{'X-Object-Manifest'}); if ($mprefix !~ /\S/ || $mcontainer !~ /\S/) { return "X-Object-Manifest header on file $file does not ". "contain a prefix : $st->{'X-Object-Manifest'}"; } my $files = &rs_list_objects($h, $mcontainer); return "Failed to find parts : $files" if (!ref($files)); foreach my $f (@$files) { if ($f =~ /^\Q$mprefix\E/ && $f ne $file) { my ($ok, $out) = &rs_api_call($h, "/$mcontainer/$f", "DELETE"); return "Failed to delete part $f : $out" if (!$ok); } } } my ($ok, $out) = &rs_api_call($h, "/$container/$file", "DELETE"); return $ok ? undef : $out; } # rs_api_call(&handle, path, method, &headers, [save-to-file], # [read-from-file|data], [file-offset], [file-length]) # Calls the rackspace API, and returns an OK flag, response body or error # message, and HTTP headers. sub rs_api_call { my ($h, $path, $method, $headers, $dstfile, $srcfile, $offset, $length) = @_; my ($host, $port, $page, $ssl) = &parse_http_url($h->{'storage-url'}); my $sendheaders = $headers ? { %$headers } : { }; $sendheaders->{'X-Auth-Token'} = $h->{'token'}; return &rs_http_call($h->{'storage-url'}.$path, $method, $sendheaders, $dstfile, $srcfile, $offset, $length); } # rs_http_call(url, method, &headers, [save-to-file], [read-from-file|data], # [file-offset], [file-length], [retry-attempts]) # Makes an HTTP call and returns an OK flag, response body or error # message, and HTTP headers. Re-tries in the face of a transient failure. sub rs_http_call { my ($url, $method, $headers, $dstfile, $srcfile, $offset, $length, $tries) = @_; $tries ||= $rs_upload_tries; local @rv; for(my $i=0; $i<$tries; $i++) { @rv = &rs_http_single_call($url, $method, $headers, $dstfile, $srcfile, $offset, $length); return @rv if ($rv[0]); # Success return @rv if ($rv[1] !~ /^(Invalid|Empty)\s+HTTP\s+response/i); sleep(10); } return @rv; # Failure after reveral re-tries } # rs_http_single_call(url, method, &headers, [save-to-file], [read-from-file|data], # [file-offset], [file-length]) # Makes an HTTP call and returns an OK flag, response body or error # message, and HTTP headers. sub rs_http_single_call { my ($url, $method, $headers, $dstfile, $srcfile, $offset, $length) = @_; my ($host, $port, $page, $ssl) = &parse_http_url($url); # Build headers my @headers; push(@headers, [ "Host", $host ]); push(@headers, [ "User-agent", "Webmin" ]); push(@headers, [ "Accept-language", "en" ]); foreach my $hname (keys %$headers) { push(@headers, [ $hname, $headers->{$hname} ]); } if ($srcfile =~ /^\// && -r $srcfile) { if ($length) { push(@headers, [ "Content-Length", $length ]); } else { my @st = stat($srcfile); push(@headers, [ "Content-Length", $st[7] ]); } } elsif (defined($srcfile)) { push(@headers, [ "Content-Length", length($srcfile) ]); } # Make the HTTP connection $main::download_timed_out = undef; local $SIG{ALRM} = \&download_timeout; alarm(60); my $h = &make_http_connection($host, $port, $ssl, $method, $page, \@headers); alarm(0); $h = $main::download_timed_out if ($main::download_timed_out); if (!ref($h)) { return (0, $h); } if ($srcfile =~ /^\// && -r $srcfile) { # Send body contents from file my $buf; open(SRCFILE, "<".$srcfile); if ($offset) { # Seek to some position seek(SRCFILE, $offset, 0); } if ($length) { # Only copy length bytes my $want = $length; while($want) { my $readlen = $want; if ($readlen > 1024*1024) { $readlen = 1024*1024; } my $got = read(SRCFILE, $buf, $readlen); &write_http_connection($h, $buf); $want -= $got; } } else { # Copy till the end of the file while(read(SRCFILE, $buf, &get_buffer_size()) > 0) { &write_http_connection($h, $buf); } } close(SRCFILE); } elsif (defined($srcfile)) { # Send body contents from string &write_http_connection($h, $srcfile); } my ($out, $error); # Read headers alarm(60); my $line; ($line = &read_http_connection($h)) =~ tr/\r\n//d; if ($line !~ /^HTTP\/1\..\s+(20[0-9])(\s+|$)/) { alarm(0); &close_http_connection($h); return (0, $line ? "Invalid HTTP response : $line" : "Empty HTTP response"); } my $rcode = $1; my %header; while(1) { $line = &read_http_connection($h); $line =~ tr/\r\n//d; $line =~ /^(\S+):\s+(.*)$/ || last; $header{$1} = $2; } alarm(0); if ($main::download_timed_out) { &close_http_connection($h); return (0, $main::download_timed_out); } # Read data my $out; if (!$dstfile) { # Append to a variable while(defined($buf = &read_http_connection($h, 1024))) { $out .= $buf; } } else { # Write to a file my $got = 0; if (!&open_tempfile(PFILE, ">$dstfile", 1)) { &close_http_connection($h); return (0, "Failed to write to $dstfile : $!"); } binmode(PFILE); # For windows while(defined($buf = &read_http_connection($h, 1024))) { &print_tempfile(PFILE, $buf); $got += length($buf); } &close_tempfile(PFILE); if ($header{'content-length'} && $got != $header{'content-length'}) { &close_http_connection($h); return (0, "Download incomplete"); } } &close_http_connection($h); return (1, $out, \%header); } 1;Private