#!/usr/bin/perl # # rsgpg.pl is a Perl script which encrypts and uploads a file to rapidshare.com # # Note: # The uploader code is based on the Perl script provided by RapidShare AG. # The original code: http://images.rapidshare.com/software/rsapiresume.pl # # Usage: ./rapidgpg.pl -f file # -r recipient # -P passphrase # -l rslogin # -p rspassword # -h # -v use strict; use warnings; use Getopt::Std; use Term::ReadKey; use GnuPG qw ( :algo ); use Digest::MD5("md5_hex"); use Fcntl; use IO::Socket; use LWP::Simple; use Crypt::SSLeay; use File::Basename; # General $SIG{PIPE} = $SIG{HUP} = 'IGNORE'; my $chunkSize = 1_000_000; my %escapes; # Process command line args my %options; getopts("l:p:P:r:f:hv", \%options); # Help required? if ($options{h}) { print <export_keys( keys => $recipient, armor => 1, output => "/tmp/export_key.$$"); }; if (! -s "/tmp/export_key.$$") { unlink("/tmp/export_key.$$"); die "Recipient's key not present in your keyring!\n"; } unlink("/tmp/export_key.$$"); ($verbose) && print STDERR "Encrypting file $srcFile ...\n"; my $dstFile = "/tmp/" . basename($srcFile) . ".gpg"; # Use eval to catch the error in case of failure eval { $gpg->encrypt( plaintext => $srcFile, output => $dstFile, armor => 1, sign => 1, recipient => $recipient, passphrase => $passPhrase); }; if ($@ && $@ =~ /BAD_PASSPHRASE/) { $errString = "Bad passphrase!"; } elsif ($@) { $errString = "Unknown error"; } ($errString) && die "Cannot encrypt file $srcFile: $errString\n"; (! -r $dstFile ) && die "Cannot read encrypted file: $dstFile\n"; $/ = undef; $| = 1; ($verbose) && print STDERR "Uploading file $dstFile ...\n"; &uploadfile($dstFile, $rsLogin, $rsPassword); unlink($dstFile); print "All done.\n"; exit; sub uploadfile { my $file = shift || die; my $login = shift || ""; my $password = shift || ""; my $realfolder = shift || 0; my ($size, $md5obj, $size2, $readbytes, $data, $md5hex, $uploadserver, $cursize, $dupefileid, $dupesize, $dupekillcode, $dupemd5hex); # This chapter checks the file and calculates the MD5HEX of the existing local file. $size = -s $file || die "File '$file' is empty or does not exist!\n"; ($verbose) && print "File $file has $size bytes. MD5="; open(FH, $file) || die "Unable to open file: $!\n"; binmode(FH); $md5obj = Digest::MD5->new; $size2 = 0; while (($readbytes = read(FH, $data, 65536)) != 0) { $size2 += $readbytes; $md5obj->add($data) } close(FH); $md5hex = $md5obj->hexdigest; ($verbose) && print "$md5hex\n"; unless ($size == $size2) { die "Strange error: $size byte found, but only $size2 byte analyzed?\n" } unless ($uploadserver) { $uploadserver = get("https://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=nextuploadserver_v1") || ""; if (not $uploadserver or $uploadserver =~ /^ERROR: /) { die "API Error occured: $uploadserver\n" } ($verbose) && print "Uploading to rs$uploadserver.rapidshare.com\n"; } $cursize = 0; while ($cursize < $size) { $cursize = &uploadchunk($file, $login, $password, $realfolder, $md5hex, $size, $cursize, "rs$uploadserver.rapidshare.com", $dupefileid, $dupekillcode) } return ""; } sub finddupes { my $login = shift || die; my $password = shift || die; my $realfolder = shift || 0; my $filename = shift || ""; my ($htmllogin, $htmlpassword, $htmlfilename, $result, $fileid, $size, $killcode, $serverid, $md5hex, $dupefileids); $htmllogin = &htmlencode($login); $htmlpassword = &htmlencode($password); $htmlfilename = &htmlencode($filename); $result = get("https://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=listfiles_v1&login=$htmllogin&password=$htmlpassword&realfolder=$realfolder&filename=$htmlfilename&fields=size,killcode,serverid,md5hex&order=fileid") || ""; if (not $result or $result =~ /^ERROR: /) { die "API Error occured: $result\n" } if ($result eq "NONE") { ($verbose) && print "FINDDUPES: No dupe detected.\n"; return (0,0,0,0,0) } foreach (split(/\n/, $result)) { unless ($_ =~ /^(\d+),(\d+),(\d+),(\d+),(\w+)/) { die "FINDDUPES: Unexpected result: $result\n" } unless ($fileid) { $fileid = $1; $size = $2; $killcode = $3; $serverid = $4; $md5hex = lc($5); next } $dupefileids .= "$fileid,"; } if ($dupefileids) { chop($dupefileids); } return ($fileid, $size, $killcode, $serverid, $md5hex); } sub uploadchunk { my $file = shift || die; my $login = shift || ""; my $password = shift || ""; my $realfolder = shift || 0; my $md5hex = shift || die; my $size = shift || die; my $cursize = shift || 0; my $fulluploadserver = shift || die; my $replacefileid = shift || 0; my $replacekillcode = shift || 0; my $bodtype = shift || 0; my ($uploaddata, $fh, $socket, $boundary, $contentheader, $contenttail, $contentlength, $header, $chunks, $chunksize, $bufferlen, $buffer, $result, $fileid, $complete, $resumed, $filename, $killcode, $remotemd5hex, $chunkmd5hex); if (-e "$file.uploaddata") { open(I, "$file.uploaddata") or die "Unable to open file: $!\n"; ($fulluploadserver, $fileid, $killcode) = split(/\n/, ); close(I); ($verbose) && print "RESUMING UPLOAD! Server=$fulluploadserver File-ID=$fileid Start=$cursize\n"; $cursize = get("htt://api.rapidshare.com/cgi-bin/rsapi.cgi?sub=checkincomplete_v1&fileid=$fileid&killcode=$killcode") || ""; unless ($cursize =~ /^\d+$/) { die "Unable to resume! Please delete $file.uploaddata or try again.\n" } $resumed = 1; } if ($size > $chunkSize) { $chunks = 1; $chunksize = $size - $cursize; if ($chunksize > $chunkSize) { $chunksize = $chunkSize } else { $complete = 1 } } else { $chunks = 0; $chunksize = $size; } sysopen($fh, $file, O_RDONLY) || die "Unable to open file: $!\n"; $filename = $file =~ /[\/\\]([^\/\\]+)$/ ? $1 : $file; $socket = IO::Socket::INET->new(PeerAddr => "$fulluploadserver:80") || die "Unable to open socket: $!\n"; $boundary = "---------------------632865735RS4EVER5675865"; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="rsapi_v1"\r\n\r\n1\r\n|; if ($resumed) { $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="fileid"\r\n\r\n$fileid\r\n|; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="killcode"\r\n\r\n$killcode\r\n|; if ($complete) { $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="complete"\r\n\r\n1\r\n| } } else { $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="login"\r\n\r\n$login\r\n|; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="password"\r\n\r\n$password\r\n|; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="realfolder"\r\n\r\n$realfolder\r\n|; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="replacefileid"\r\n\r\n$replacefileid\r\n|; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="replacekillcode"\r\n\r\n$replacekillcode\r\n|; $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="bodtype"\r\n\r\n$bodtype\r\n|; if ($chunks) { $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="incomplete"\r\n\r\n1\r\n| } } $contentheader .= qq|$boundary\r\nContent-Disposition: form-data; name="filecontent"; filename="$filename"\r\n\r\n|; $contenttail = "\r\n$boundary--\r\n"; $contentlength = length($contentheader) + $chunksize + length($contenttail); if ($resumed) { $header = qq|POST /cgi-bin/uploadresume.cgi HTTP/1.1\r\nHost: $fulluploadserver\r\nContent-Type: multipart/form-data; boundary=$boundary\r\nContent-Length: $contentlength\r\n\r\n|; } else { $header = qq|POST /cgi-bin/upload.cgi HTTP/1.1\r\nHost: $fulluploadserver\r\nContent-Type: multipart/form-data; boundary=$boundary\r\nContent-Length: $contentlength\r\n\r\n|; } print $socket "$header$contentheader"; sysseek($fh, $cursize, 0); $bufferlen = sysread($fh, $buffer, $chunkSize) || 0; unless ($bufferlen) { die "Error while reading file: $!\n" } $chunkmd5hex = md5_hex($buffer); ($verbose) && print "Sending $bufferlen byte... "; $cursize += $bufferlen; print $socket $buffer; print $socket $contenttail; ($result) = <$socket> =~ /\r\n\r\n(.+)/s; unless ($result) { die "Ooops! Did not receive any valid server results?\n" } if ($resumed) { if ($complete) { if ($result =~ /^COMPLETE,(\w+)/) { ($verbose) && print "Upload completed! Checking MD5...\nRemote MD5=$1 Local MD5=$md5hex\n"; if ($md5hex ne $1) { die "MD5 CHECK NOT PASSED!\n" } ($verbose) && print "MD5 check passed. Upload OK!\n"; unlink("$file.uploaddata"); } else { die "Unexpected server response!\n"; } } else { if ($result =~ /^CHUNK,(\d+),(\w+)/) { ($verbose) && print "Chunk upload completed! $1 byte uploaded.\nRemote MD5=$2 Local MD5=$chunkmd5hex\n\n"; if ($2 ne $chunkmd5hex) { die "CHUNK MD5 CHECK NOT PASSED!\n" } } else { die "Unexpected server response!\n\n$result\n"; } } } else { if ($result =~ /files\/(\d+)/) { $fileid = $1 } else { die "Server result did not contain a file ID.\n$result" } unless ($result =~ /File1\.3=(\d+)/ and $1 == $cursize) { die "Server did not save all data we sent.\n$result" } unless ($result =~ /File1\.2=.+?killcode=(\d+)/) { die "Server did not send our killcode.\n$result" } $killcode = $1; unless ($result =~ /File1\.4=(\w+)/) { die "Server did not send the remote MD5 sum.\n" } $remotemd5hex = lc($1); if ($chunks) { if ($result !~ /File1\.5=Incomplete/) { die "Server did not acknowledge the incomplete upload request.\n" } ($verbose) && print "Chunk upload completed! $cursize byte uploaded.\nRemote MD5=$remotemd5hex Local MD5=$chunkmd5hex\n"; if ($remotemd5hex ne $chunkmd5hex) { die "CHUNK MD5 CHECK NOT PASSED!\n" } ($verbose) && print "Upload OK! Resuming upload...\n\n"; open(O, ">$file.uploaddata") or die "Unable to save upload server: $!\n"; print O "$fulluploadserver\n$fileid\n$killcode\n"; close(O); } else { if ($result !~ /File1\.5=Completed/) { die "Server did not acknowledge the completed upload request.\n" } if ($md5hex ne $remotemd5hex) { die "FINAL MD5 CHECK NOT PASSED! LOCAL=$md5hex REMOTE=$remotemd5hex\n" } ($verbose) && print "FINAL MD5 check passed. Upload OK!\n"; $result =~ /File1\.1=(.+)/; print "File URL: $1\n"; } } return $cursize; } sub htmlencode { my $text = shift || ""; unless (%escapes) { for (0 .. 255) { $escapes{chr($_)} = sprintf("%%%02X", $_) } } $text =~ s/(.)/$escapes{$1}/g; return $text; }