File: //scripts.20110531.215904.25158/getremotecpmove
#!/usr/bin/perl
# cpanel - getremotecpmove Copyright(c) 2010 cPanel, Inc.
# All rights Reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
BEGIN {
unshift @INC, '/usr/local/cpanel';
$SIG{__WARN__} = sub {
my $errlt = localtime( time() );
my $safe_longmess = Carp::longmess(@_);
$safe_longmess =~ s/('pass'\,?\s+\')[^\']+/$1__HIDDEN__/g;
print "(internal error) $errlt [$$] warn: " . $safe_longmess;
};
$SIG{__DIE__} = sub {
my $errlt = localtime( time() );
my $safe_longmess = Carp::longmess(@_);
$safe_longmess =~ s/('pass'\,?\s+\')[^\']+/$1__HIDDEN__/g;
print "(internal error) $errlt [$$] error: " . $safe_longmess;
};
}
use strict;
use Socket ();
use Cpanel::HttpRequest ();
use Cpanel::Encoder::URI ();
use Cpanel::Encoder::Tiny ();
use Cpanel::MD5 ();
use Cpanel::Filesys ();
$| = 1;
my $httpClient = Cpanel::HttpRequest->new( 'hideOutput' => 0 );
chdir("/scripts");
my $host = $ARGV[0];
my $user = $ARGV[1];
my $domain = $ARGV[2];
my $usebestdisk = $ARGV[3];
my $permitfallb = $ARGV[4];
$host =~ s/\///g;
$user =~ s/\///g;
$domain =~ s/\///g;
$host =~ s/\.\.//g;
$user =~ s/\.\.//g;
$domain =~ s/\.\.//g;
$domain =~ s/[\s\n\t\r]*//g;
my $pass = <STDIN>;
$pass =~ s/\n//g;
my $extractdir;
my $md5sum;
my $part = '/home';
if ($usebestdisk) {
$part = Cpanel::Filesys::getmntpoint();
}
my @PKGDEBUG;
my ( $fetch_ok, $archive_file, $extractdir, $md5sum, $pkgdebug ) = fetch_acct_by_cpanel( 'user' => $user, 'host' => $host, 'domain' => $domain, 'pass' => $pass, 'part' => $part );
push @PKGDEBUG, $pkgdebug;
if ( !$fetch_ok ) {
print "Failed to fetch cpmove file via cPanel XML-API.\n";
if ($permitfallb) {
( $fetch_ok, $archive_file, $extractdir, $md5sum, $pkgdebug ) = fetch_acct_by_ftp_and_http( 'user' => $user, 'host' => $host, 'domain' => $domain, 'pass' => $pass, 'part' => $part );
push @PKGDEBUG, $pkgdebug;
} else {
print "Terminating because fallback to ftp+cgi was not enabled.\n";
exit;
}
}
if ($fetch_ok) {
if ($md5sum) {
my $newmd5 = Cpanel::MD5::getmd5sum($archive_file);
if ( $newmd5 eq $md5sum ) {
print "Checksum Matches!\n";
}
else {
print "Checksum Failure [[$newmd5]] [[$md5sum]]...trace information follows...<table width=\"100%\" style=\"border: 1px #000 solid;\"><tr><td><pre>" . join( "\n\n\n", @PKGDEBUG ) . "</pre></td></tr></table>\n\n";
exit;
}
}
elsif ( -z $archive_file ) {
print "Checksum Failure: Failed to download account file.\n";
exit;
}
print "extract dir name is: $extractdir\n";
print "pkgacctfile is: $archive_file\n";
print "MOVE IS GOOD!\n";
}
else {
print "Failed to fetch account via cpanel and ftp/web\n";
}
sub fetch_acct_by_cpanel {
my %OPTS = @_;
my $host = $OPTS{'host'};
my $user = $OPTS{'user'};
my $pass = $OPTS{'pass'};
my $part = $OPTS{'part'};
print "Trying to fetch cpmove file via cPanel XML-API!\n";
require MIME::Base64;
require Net::SSLeay;
{
local $SIG{'__DIE__'};
require IO::Socket::SSL;
IO::Socket::SSL->import('inet4'); # see case 16674
}
print "Fetching current backups from remote server....";
my ($current_bck_page,$current_bck_response,$current_bck_ref) = get_current_backups( $host, $user, $pass );
print ".." . (scalar keys %$current_bck_ref) . " backups found...";
print "Done\n";
foreach my $bck (keys %$current_bck_ref) {
if ($current_bck_ref->{$bck}) {
print "A backup to the file $bck is currently in progress on the remote server.\n";
print "Please wait until it is complete and try again.\n";
exit 1;
}
}
if ($current_bck_response =~ /401/) {
return 0;
}
print "Starting the backup...";
my $encoded_request = '/xml-api/cpanel?xmlin=%3Ccpanelaction%3E%3Capiversion%3E1%3C/apiversion%3E%3Cmodule%3EFileman%3C/module%3E%3Cfunc%3Efullbackup%3C/func%3E%3C/cpanelaction%3E';
my $decoded_request = Cpanel::Encoder::URI::uri_decode_str($encoded_request);
#start the backup
my ( $page, $response, %reply_headers ) = Net::SSLeay::get_https( $host, 2083,
$encoded_request,
Net::SSLeay::make_headers( Authorization => 'Basic ' . MIME::Base64::encode( "$user:$pass", '' ) ) );
print "Done\n";
print "Waiting for backup to start...";
sleep(10);
print "Done\n";
print "Checking remote server for backups...";
my ($new_bck_page,$new_bck_response,$new_bck_ref) = get_current_backups( $host, $user, $pass );
print ".." . (scalar keys %$new_bck_ref) . " backups found.\n";
my $backup_file;
foreach my $back ( keys %{$new_bck_ref} ) {
if ( !exists $current_bck_ref->{$back} ) {
$backup_file = $back;
last;
}
}
if ( !$backup_file ) {
print "Failed to start the backup on the remote machine (if a previous backup is in progress you will need to wait until it is complete)!\n";
print "(Trace information follows for backup start request)...<table width=\"100%\" style=\"border: 1px #000 solid;\"><tr><td><pre>" . Cpanel::Encoder::Tiny::safe_html_encode_str($decoded_request) . "</pre></td></tr><td><pre>" . Cpanel::Encoder::Tiny::safe_html_encode_str($page) . "</pre></td></tr></table>\n";
print "(Trace information follows for initial backups)...<table width=\"100%\" style=\"border: 1px #000 solid;\"><tr><td><pre>" . Cpanel::Encoder::Tiny::safe_html_encode_str($current_bck_page) . "</pre></td></tr></table>\n";
print "(Trace information follows for backups after request started)...<table width=\"100%\" style=\"border: 1px #000 solid;\"><tr><td><pre>" . Cpanel::Encoder::Tiny::safe_html_encode_str($new_bck_page) . "</pre></td></tr></table>\n";
return (0);
}
print "Remote server is creating backup to $backup_file!\n";
print "Starting wait cycle for remote backup.\n";
my $bck_ref;
my $bck_page;
my $bck_response;
for ( 0 .. 1500 ) {
($bck_page,$bck_response,$bck_ref) = get_current_backups( $host, $user, $pass );
if ( exists $bck_ref->{$backup_file} ) {
if ( !$bck_ref->{$backup_file} ) {
last;
}
else {
print "Backup into $backup_file is still in progress.\nChecking again in 60 seconds...\n";
sleep(15);
print "Checking again in 45 seconds...\n";
sleep(15);
}
}
else {
print "Backup file $backup_file went away!\n";
return (0);
}
print "Checking again in 30 seconds...\n";
sleep(15);
print "Checking again in 15 seconds...\n";
sleep(15);
}
print "Downloading $backup_file....";
chdir($part) || return (0);
my $client = IO::Socket::SSL->new("$host:2083") || do {
print "Could not connect to $host on port 2083\n";
return (0);
};
my $now = time();
my $dest = "cpmove-$user-$now.tmp";
print {$client} "GET /download/$backup_file HTTP/1.0\r\n";
print {$client} "Connection: close\r\n";
print {$client} ( "Authorization: Basic " . MIME::Base64::encode_base64( $user . ':' . $pass ) . "\r\n\r\n" );
my $cl = 1;
my $percent = 0;
open( my $out_fh, '>', $dest );
while ( readline($client) ) {
alarm(20);
if (/^content-length: (\d+)/i) {
print;
$cl = $1;
}
last if (/^[\r\n]*$/);
}
alarm(0);
my $bytes;
my $new_percent;
my $cc = 0;
my $buffer;
my $bytesread = 0;
while ( $bytes = read( $client, $buffer, 65536 ) ) {
alarm(90);
$bytesread += $bytes;
$cc++;
if ( $cc == 170 ) {
$new_percent = int( ( $bytesread / $cl ) * ( $cl == 1 ? 1 : 100 ) );
if ( $new_percent != $percent ) {
$percent = $new_percent;
print "..${percent}" . ( $cl == 1 ? '' : '%' ) . "..\n";
}
$cc = 0;
}
print {$out_fh} $buffer;
}
alarm(0);
close($out_fh);
close($client);
print "Done\n";
my $extractdir = $backup_file;
$extractdir =~ s/(\.tar)?(\.gz)?$//g;
system( '/bin/mv', '-f', $dest, "$part/cpmove-$user-$now.tar.gz" );
return ( 1, "$part/cpmove-$user-$now.tar.gz", $extractdir, '', '' );
}
sub get_current_backups {
my ( $host, $user, $pass ) = @_;
#look for backups
my %CURRENT_BACKUPS;
my ( $page, $response, %reply_headers ) = Net::SSLeay::get_https( $host, 2083, '/xml-api/cpanel?xmlin=%3Ccpanelaction%3E%3Capiversion%3E1%3C/apiversion%3E%3Cmodule%3EFileman%3C/module%3E%3Cfunc%3Elistfullbackups%3C/func%3E%3C/cpanelaction%3E', Net::SSLeay::make_headers( Authorization => 'Basic ' . MIME::Base64::encode( "$user:$pass", '' ) ) );
if ($response =~ /401/) {
print "cPanel Login Failed\n";
return ($page,$response,{});
}
my $stripped_page = $page;
$stripped_page =~ s/\<[^\>]+\>/\n/g; #strip xml
$stripped_page = Cpanel::Encoder::Tiny::safe_html_decode_str($stripped_page);
foreach my $req ( split( /\n/, $stripped_page ) ) {
if ( $req =~ /(backup-[^\"\>]+)/ ) {
my $file = $1;
$file =~ s/\s*$//g;
my ( $bckfile, $progress ) = split( /\s+/, $file, 2 );
$CURRENT_BACKUPS{$bckfile} = ( $progress =~ /progress/i ? 1 : 0 );
}
}
return ($page,$response,\%CURRENT_BACKUPS);
}
sub fetch_acct_by_ftp_and_http {
my %OPTS = @_;
my $host = $OPTS{'host'};
my $domain = $OPTS{'domain'};
my $user = $OPTS{'user'};
my $pass = $OPTS{'pass'};
my $part = $OPTS{'part'};
print "Trying to fetch cpmove file via ftp+cgi!\n";
my $md5sum;
my $archive_name;
my $uid;
$SIG{'ALRM'} = sub {
print "Copy Session Timed out!\n";
die;
};
alarm(7200);
require Net::FTP;
my $ftp = Net::FTP->new( "$host", Debug => 0 );
print "Attempting to login as $user to $host via ftp\n";
if ( $ftp->login( $user, $pass ) ) {
print "Login ok\n";
}
else {
print "FTP Login Failed\n";
exit;
}
$ftp->mkdir( "public_html/cgi-bin/cpdownload", 1 );
$ftp->site("chmod 755 public_html/cgi-bin/cpdownload");
$ftp->cwd("public_html/cgi-bin/cpdownload");
print "Uploading Htaccess\n";
$ftp->put(".htaccess");
print "Uploading Wrapper\n";
$ftp->put("cpanelwrap.c");
print "Uploading Dectector\n";
$ftp->put("cpanelwrap.cgi");
print "Uploading Packager\n";
$ftp->put("cpaneldownload.cgi");
print "Uploading Downloader\n";
$ftp->put("cpaneldownacct.cgi");
print "Uploading Killer\n";
$ftp->put("cpanelkill.cgi");
print "Chmoding scripts\n";
$ftp->site("chmod 755 cpanelwrap.cgi");
$ftp->site("chmod 755 cpaneldownacct.cgi");
$ftp->site("chmod 755 cpaneldownload.cgi");
$ftp->site("chmod 755 cpanelkill.cgi");
my $odebug = '';
my $hassuexec = 0;
my $script = 'cpanelwrap.cgi';
my @FOPTS = ( [ $host, "/~${user}/cgi-bin/cpdownload/${script}?${user}" ], [ $host, "/cgi-bin/cpdownload/${script}?${user}" ] );
my $req;
foreach my $hoste (@FOPTS) {
my ( $host, $url ) = @{$hoste};
$req = $httpClient->request(
'host' => $host,
'url' => $url,
'protocol' => 0,
);
next if ( $req !~ /UID/ );
foreach ( split( /\n/, $req ) ) {
$odebug .= $_;
if (/MYUID: (\d+)/) {
$uid = $1;
}
if (/REALUID: (\d+)/) {
if ( $1 == $uid ) {
$hassuexec = 1;
}
}
}
last;
}
if ($uid) {
print "Found uid to be: $uid\n";
}
else {
print "Unable to get uid of remote account...(trace information follows)...<table width=\"100%\" style=\"border: 1px #000 solid;\"><tr><td><pre>$odebug</pre><br />" . Cpanel::Encoder::Tiny::safe_html_encode_str($req) . "</td></tr></table>\n";
exit;
}
mkdir( "/root/cpmove", 0700 );
mkdir( "/root/cpmove/$host-$uid", 0700 );
chdir("/root/cpmove/$host-$uid");
print "Compiling wrapper\n";
system( "gcc", '-m32',"-DUID=$uid", "/scripts/cpanelwrap.c", "-o", "cpanelwrapper.cgi" );
system( "gcc", '-m32',"-DUID=$uid", "/scripts/cpanelwrap2.c", "-o", "cpanelkiller.cgi" );
print "Uploading wrapper\n";
$ftp->put("cpanelwrapper.cgi");
$ftp->put("cpanelkiller.cgi");
print "Chmodding wrapper\n";
$ftp->site("chmod 4755 cpanelwrapper.cgi");
$ftp->site("chmod 4755 cpanelkiller.cgi");
unlink("cpanelwrapper.cgi");
chdir("/scripts");
system( "/bin/rm", "-rf", '--', "/root/cpmove/$host-$uid" );
print "Closing FTP Control\n";
$ftp->quit;
my $proto = getprotobyname('tcp');
socket( Socket_Handle, &Socket::AF_INET, &Socket::SOCK_STREAM, $proto );
my $iaddr = gethostbyname($host);
my $port = getservbyname( 'http', 'tcp' );
my $sin = Socket::sockaddr_in( $port, $iaddr );
connect( Socket_Handle, $sin );
alarm(7200);
if ( !$hassuexec ) {
print "Packing Account using non-suexec method\n";
send Socket_Handle, "POST /~${user}/cgi-bin/cpdownload/cpanelwrapper.cgi HTTP/1.0\n", 0;
}
else {
print "Packing Account using suexec method\n";
send Socket_Handle, "POST /~${user}/cgi-bin/cpdownload/cpaneldownload.cgi HTTP/1.0\n", 0;
}
if ( $domain ne "" ) {
send Socket_Handle, "Host: ${domain}\n", 0;
}
my $ct = "user=${user}&pass=${pass}";
my $cl = length($ct);
send Socket_Handle, "Content-length: ${cl}\n", 0;
send Socket_Handle, "Connection: close\n\n", 0;
send Socket_Handle, $ct, 0;
my $pkgdebug = '';
my $inheaders = 1;
my $archive_name;
my $md5sum;
while (<Socket_Handle>) {
$pkgdebug .= $_;
s/\n//g;
if ( !$inheaders ) {
if (/^md5sum is: (\S+)/) {
$md5sum = $1;
print "FOUND MD5 to be : $md5sum\n";
}
if (/^\s*DOWNLOAD\s+READY\s+in\s+(\S+)/) {
($archive_name) = $1 =~ m/([^\/]+)$/;
$archive_name =~ s/[.]tar[.]gz$//;
}
print;
print "\n";
}
if ( /^$/ || /Content-Type/ ) { $inheaders = 0; }
}
shutdown( Socket_Handle, 2 );
close(Socket_Handle);
print "Downloading Tarball\n";
chdir $part || return (0);
mkdir( "cptmp", 0700 );
chdir("$part/cptmp") || return (0);
mkdir( $user, 0700 );
chdir $user || return (0);
alarm(7200);
$httpClient->download( "http://$host/~${user}/cgi-bin/cpdownload/cpaneldownacct.cgi?${user}", "cpmove-${user}.tmp" );
my $script = '';
if ( !$hassuexec ) {
$script = "cpanelkiller.cgi";
print "Removing Scripts using non-suexec method\n";
}
else {
$script = "cpanelkill.cgi";
print "Removing Scripts using suexec method\n";
}
my @FOPTS = ( [ $host, "/~${user}/cgi-bin/cpdownload/${script}?${user}" ], [ $host, "/cgi-bin/cpdownload/${script}?${user}" ] );
foreach my $hoste (@FOPTS) {
my ( $host, $url ) = @{$hoste};
my $req = $httpClient->request(
'host' => $host,
'url' => $url,
'protocol' => 0,
);
if ( $req =~ /UNLINKED/i ) {
print "Removed archive from remote server ok!\n";
last;
}
}
if ( !-e "cpmove-${user}.tmp" || -z _ ) {
print "Failed to download account package!\n";
unlink "cpmove-${user}.tmp";
return ( 0, '', '', $pkgdebug );
}
my $now = time();
system( '/bin/mv', '-f', '--', "cpmove-$user.tmp", "$part/cpmove-$user-$now.tar.gz" );
alarm(0);
return ( 1, "$part/cpmove-$user-$now.tar.gz", 'cpmove-' . $user, $md5sum, $pkgdebug );
}