#!/usr/bin/perl ### Scott Sullivan (ssullivan@liquidweb.com) # suphpfix # # GENERAL: # Fixes common suPHP/FCGI/CGI (with suexec enabled) permission/ownership issues on cPanel servers. For specifics, see the help function. # USAGE: # See the help function. use strict; our $version="2.0.1"; use JSON; use LWP::UserAgent; use Encode; use Term::ANSIColor; use Fcntl ':mode'; use POSIX qw(strftime); use Linux::Ext2::FileAttributes; sub help { print color 'red'; print "*** Options: \n\n"; print color 'green'; print "+++ Fix common problems when converting to suPHP\n"; print color 'reset'; print color 'yellow'; print "--prep all "; print color 'blue'; print "==> "; print color 'reset'; print " This will chown all cPanel users files/directories in their public_html's to cPanelUser.cPanelUser. It will also remove group and world write from files/directories. In addition, any php directive in .htaccess such as php_flag will be commented out (unless htscanner is present). \n"; print color 'yellow'; print "--prep cPanelUser "; print color 'blue'; print "==> "; print color 'reset'; print "Same as '--prep all' but only applies fixes to the specified cPanel account.\n"; print color 'green'; print "\n+++ Saving states\n"; print color 'reset'; print color 'yellow'; print "--save-state all "; print color 'blue'; print "==> "; print color 'reset'; print "This saves the current permission/ownership settings (for later restores) for all cPanel accounts files/directories under their public_html's.\n"; print color 'yellow'; print "--save-state cPanelUser"; print color 'blue'; print " ==> "; print color 'reset'; print "Same as '--save-state all' but only for the specified cPanel account.\n"; print color 'green'; print "\n+++ Restoring states\n"; print color 'reset'; print color 'yellow'; print "--restore-state all "; print color 'blue'; print "==> "; print color 'reset'; print "This restores all saved cPanel accounts permission/ownership settings at the time --save-state all was last ran. It also uncomments any php directives in accounts .htaccess file(s).\n"; print color 'yellow'; print "--restore-state all verify"; print color 'blue'; print " ==> "; print color 'reset'; print "Same as '--restore-state all' but just outputs proposed chmods/chowns for all accounts without actually changing anything.\n"; print color 'yellow'; print "--restore-state cPanelUser"; print color 'blue'; print " ==> "; print color 'reset'; print "This restores only the specified cPanel accounts permissions/ownership settings at the time --save-state was last ran for the user. It also uncomments any php directives in the accounts .htaccess file(s).\n"; print color 'yellow'; print "--restore-state cPanelUser verify"; print color 'blue'; print " ==> "; print color 'reset'; print "Same as '--restore-state cPanelUser' but just outputs proposed chmods/chowns for the user without actually changing anything.\n"; print color 'red'; print "\n*** General: \n\n"; print color 'reset'; print color 'yellow'; print "* "; print color 'reset'; print "suphpfix uses /root/datastore_suphpfix/ to store JSON data. These JSON files allow suphpfix to store what permissions/ownerships each backed up cPanel user had at the time of the last --save-state. There are two JSON files for each cPanel account (one for files, one for directories). In this directory suphpfix uses backedupUserList.all to store what cPanel users were backed up with --save-state all. \n\n"; } sub runCmds { die "FATAL: wrong args passed to runCmds()!\n" if (scalar(@_) != 4); my $user = $_[0]; my $domain = $_[1]; my $docRoot = $_[2]; my $htscannerCheck = $_[3]; print color 'reset'; print "---------------------------------", "\n"; print color 'yellow'; print "Working with: "; print color 'reset'; print $domain, "\n"; print color 'yellow'; print "Discovered document root: "; print color 'reset'; print $docRoot, "\n"; print "++ Removing group and world write in ", $docRoot, " ...\n"; system("find $docRoot -perm +022 -exec chmod go-w {} \\\;"); print color 'green'; print "* Task complete.\n"; print color 'reset'; print "++ Checking directory permissions in ", $docRoot, " ...\n"; system("find $docRoot -type d -exec chmod 755 {} \\\;"); print color 'green'; print "* Task complete.\n"; print color 'reset'; print "++ Setting ownerships to ", $user, ":", $user, " in ", $docRoot, " ...\n"; system("chown -R $user:$user $docRoot"); if ( $? != 0 ) { print color 'red'; print "Error in chown command: chown -R $user:$user $docRoot\n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("chown $user:nobody $docRoot"); if ( $? != 0 ) { print color 'red'; print "Error in chown command: chown $user:nobody $docRoot\n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print color 'green'; print "* Task complete.\n"; print color 'reset'; print "++ Ensuring files are readable in $docRoot ... \n"; system("find $docRoot -type f -exec chmod ugo+r {} \\\;"); print color 'green'; print "* Task complete.\n"; print color 'reset'; if ($htscannerCheck eq "Installed") { print color 'reset'; print color 'yellow'; print "NOTICE: htscanner found, keeping php tweaks. \n"; print color 'reset'; } elsif ($htscannerCheck eq "NotInstalled") { print "++ Commenting out php tweaks from .htaccess ...", "\n"; system("find $docRoot -name .htaccess -exec sed -i 's/^php_/#php_/g' {} \\\;"); print color 'green'; print "* Task complete.", "\n"; print color 'reset'; } print "Done with ", $domain, "!", "\n"; print "------------------------------", "\n"; } sub cleanUp { print color 'green'; print "\nCleaning up... \n"; system("chmod -R 1777 /tmp"); system("chmod 700 /tmp/screens/S-root &> /dev/null"); print "Completed. \n"; print color 'reset'; } sub suphpfixMain { die "FATAL: wrong args passed to suphpfixMain()!\n" if (scalar(@_) != 1); my $param = $_[0]; print "Generating hash..."; system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $hash = `cat /root/.accesshash`; print color 'green'; print "success!\n"; print color 'reset'; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); my $accntList; eval{ $accntList = $json->allow_nonref->utf8->relaxed->decode($list); }; if ($@) { print color 'red'; print "ERROR: Didn't receive a valid JSON response from cPanel API.\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $totalAccnts="0"; for my $each( @{$accntList->{acct}} ) { $totalAccnts++; }; print color 'reset'; print "Checking for htscanner..."; my $htscannerChk = `php -i |grep -i htscanner |grep htscanner.config_file|cut -f 1 -d "="`; my $htscannerReturnChk = ""; if (!$htscannerChk) { print color 'yellow'; print "not found! \n"; print color 'reset'; $htscannerReturnChk = "NotInstalled"; } else { print color 'green'; print "found! \n"; print color 'reset'; $htscannerReturnChk = "Installed"; } #### #### Single user #### if ( defined($param) ) { if ( $param ne "all" ) { if ($totalAccnts >= 2) { my $validUser=""; my $singleUserPartition; my $singleUserDocRoot; my $singleUserDomain; for my $userCnt( @{$accntList->{acct}} ) { if ( $userCnt->{user} eq $param ) { $validUser = "true"; $singleUserPartition = $userCnt->{partition}; $singleUserDocRoot = "/$singleUserPartition/$param/public_html "; $singleUserDomain = "$userCnt->{domain}"; } } if ( $validUser ne "true" ) { print color 'reset'; print color 'red'; print "ERROR: $param not found to be a valid user. \n"; print color 'reset'; exit(1); } if ( $validUser eq "true" ) { &runCmds("$param","$singleUserDomain","$singleUserDocRoot","$htscannerReturnChk"); &cleanUp(); exit; } } elsif ( $totalAccnts == 1 ) { print color 'reset'; print color 'yellow'; print "NOTICE: You only have one account, no need to specify user. Please run with '--prep all'.\n"; print color 'reset'; exit(1); } else { print color 'reset'; print color 'yellow'; print "NOTICE: You do not appear to have any accounts setup yet.\n"; print color 'reset'; exit(1); } } } ### ### All users ### if ($totalAccnts == "1") { print color 'reset'; print "Discovered one account.\n"; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); eval{ $accntList = $json->allow_nonref->utf8->relaxed->decode($list); }; if ($@) { print color 'red'; print "ERROR: Didn't receive a valid JSON response from cPanel API.\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $user; my $domain; my $partition; for my $users( @{$accntList->{acct}} ) { $user = $users->{user}; $domain = $users->{domain}; $partition = $users->{partition}; }; my $docRoot = "/$partition/$user/public_html"; &runCmds("$user","$domain","$docRoot","$htscannerReturnChk"); &cleanUp(); exit; } elsif ($totalAccnts >= "2") { print "Discovered multiple accounts.\n"; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); eval{ $accntList = $json->allow_nonref->utf8->relaxed->decode($list); }; if ($@) { print color 'red'; print "ERROR: Didn't receive a valid JSON response from cPanel API.\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my ($domain,$user,$partition,$docRoot); my $count="0"; for my $userCnt( @{$accntList->{acct}} ) { $user = $userCnt->{user}; $domain = $userCnt->{domain}; $partition = $userCnt->{partition}; $docRoot = "/$partition/$user/public_html"; print color 'reset'; &runCmds("$user","$domain","$docRoot","$htscannerReturnChk"); $count++; print color 'reset'; print color 'green'; print "Completed: $count / $totalAccnts \n"; print color 'reset'; } &cleanUp(); } else { print color 'yellow'; print "NOTICE: You do not appear to have any accounts setup yet.\n"; print color 'reset'; exit(1); } } sub getCPuserDetails { die "getCPuserDetails() needs one param. Param1: cPanel user or 'all' \n" if (scalar(@_) != 1); my $arg1 = $_[0]; system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $hash = `cat /root/.accesshash`; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; if ( $arg1 eq "all" ) { my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); our @cpUsersArray; my $accountCounter = 0; for my $Cnt( @{$accntList->{acct}} ) { $accountCounter++; } if ( $accountCounter == 0 ) { print color 'yellow'; print "NOTICE: You do not appear to have any accounts setup yet. \n"; print color 'reset'; exit(1); } for my $userCnt( @{$accntList->{acct}} ) { my $cpUser = $userCnt->{user}; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Saving state for $cpUser\n"; print color 'reset'; my $docRoot = "/" , $userCnt->{partition} , "/" , $userCnt->{user} , "/public_html \n"; my $partition = $userCnt->{partition}; my $docRoot = "/$partition/$cpUser/public_html"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "$cpUser document root is: $docRoot \n"; push(@cpUsersArray, $cpUser); &findFiles("$cpUser","$docRoot"); &findDirs("$cpUser","$docRoot"); } my $backedUpUsersSave = '/root/datastore_suphpfix/backedupUserList.all'; print color 'red'; open BAKUSERS, ">$backedUpUsersSave" or die "ERROR: Could not open $!\n"; print color 'reset'; my $endCount = "0"; for my $user ( @cpUsersArray ) { print BAKUSERS "$user\n"; $endCount++; }; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Compled. Saved state for $endCount accounts. \n"; print color 'reset'; } else { ## Verify passed arg is valid cpUser my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); our @cpUsersArray; my $isValid; for my $userCnt( @{$accntList->{acct}} ) { if ( $userCnt->{user} eq $arg1 ) { my $cpUser = $userCnt->{user}; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Saving state for $cpUser\n"; print color 'reset'; $isValid = "true"; my $docRoot = "/" , $userCnt->{partition} , "/" , $userCnt->{user} , "/public_html \n"; my $partition = $userCnt->{partition}; my $docRoot = "/$partition/$cpUser/public_html"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "$cpUser document root is: $docRoot \n"; &findFiles("$cpUser","$docRoot"); &findDirs("$cpUser","$docRoot"); } } if ( $isValid ne "true" ) { print color 'red'; print "ERROR: $arg1 is not a valid cPanel user.\n"; print color 'reset'; exit(1); } } } sub savePermOwner { die "savePermOwner() needs three params. Param1: file to parse through. Param2: files/dirs. Param3: cPanelUser. \n" if (scalar(@_) != 3); my $file = $_[0]; our $passFrom = $_[1]; my $cpUser = $_[2]; #remove blank lines $file =~ s/\n+/\n/g; open(FILEparse, "<$file"); my $count = "0"; my ($line, $permissions, $user, $group); our %HashOfArrayFilePerms = ( acct => { cpanelUser => [ ], file => [ ], perm => [ ], owner => [ ], group => [ ], }, ); our %HashOfArrayDirPerms = ( acct => { cpanelUser => [ ], file => [ ], perm => [ ], owner => [ ], group => [ ], }, ); while () { ($line) = $_; chomp($line); if ((my $dev,my $ino,my $mode,my $nlink,my $uid,my $gid,my $rdev,my $size,my $atime,my $mtime,my $ctime,my $blksize,my $blocks) = lstat($line)) { $user = getpwuid($uid); $group = getgrgid($gid); $permissions = sprintf "%04o", S_IMODE($mode); if ( $passFrom eq "files" ) { ##Push the data to the hash of array push ( @{$HashOfArrayFilePerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayFilePerms{'acct'}{'file'}}, "$line"); push ( @{$HashOfArrayFilePerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayFilePerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayFilePerms{'acct'}{'group'}}, "$group"); } elsif ( $passFrom eq "dirs" ) { ##Push the data to the hash of array push ( @{$HashOfArrayDirPerms{'acct'}{'cpanelUser'}}, "$cpUser"); push ( @{$HashOfArrayDirPerms{'acct'}{'file'}}, "$line"); push ( @{$HashOfArrayDirPerms{'acct'}{'perm'}}, "$permissions"); push ( @{$HashOfArrayDirPerms{'acct'}{'owner'}}, "$user"); push ( @{$HashOfArrayDirPerms{'acct'}{'group'}}, "$group"); } $count++; } else { print color 'red'; print "ERROR: unable to parse $line \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } my $json_text; our $HashOfArrayFilePermsRef; our $HashOfArrayDirPermsRef; if ( $passFrom eq "files" ) { $HashOfArrayFilePermsRef = \%HashOfArrayFilePerms; $json_text = JSON->new->utf8(1)->pretty(0)->allow_nonref->encode($HashOfArrayFilePermsRef); } elsif ( $passFrom eq "dirs" ) { $HashOfArrayDirPermsRef = \%HashOfArrayDirPerms; $json_text = JSON->new->utf8(1)->pretty(0)->allow_nonref->encode($HashOfArrayDirPermsRef); } my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } system("rm -f $file"); if ($passFrom eq "files") { print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "++ Recorded $count files permissions/ownerships for $cpUser\n"; print color 'reset'; our $json_datastore = $saveDir . '/datastore_file_suphpfix.' . $cpUser .'.JSON'; print color 'red'; open FILE, ">$json_datastore" or die "ERROR: Could not open $json_datastore $!"; print color 'reset'; print FILE $json_text , "\n"; close(FILE); } elsif ($passFrom eq "dirs") { print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "++ Recorded $count directories permissions/ownerships for $cpUser\n"; print color 'reset'; our $json_datastore = $saveDir . '/datastore_dir_suphpfix.' . $cpUser . '.JSON'; print color 'red'; open FILE, ">$json_datastore" or die "ERROR: Could not open $json_datastore $! \n"; print color 'reset'; print FILE $json_text , "\n"; close(FILE); } } sub findFiles { die "findFiles() requires cpanel user as arg1 and wwwRoot as arg2\n" if (scalar(@_) != 2); my $cpUser = $_[0]; my $path = $_[1]; my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } my $fileParseLoc = "$saveDir/suphpfixFileStore.txt"; if ( $path eq "" || !defined($path) ) { print color 'reset'; print "ERROR: Unable to locate file under account $cpUser !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } if ( ! -e "$path") { print color 'red'; print "ERROR: File $path doesn't exist for account $cpUser !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Generating file list for $path ... \n"; system("find $path -type f > $fileParseLoc"); &savePermOwner("$fileParseLoc","files","$cpUser"); } sub findDirs { die "findDirs() requires cpanel user as arg1 and wwwRoot as arg2\n" if (scalar(@_) != 2); my $cpUser = $_[0]; my $path = $_[1]; my $saveDir = "/root/datastore_suphpfix"; if (! -d "$saveDir") { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "NOTICE: $saveDir not found, creating it... \n"; print color 'reset'; system("mkdir $saveDir"); } my $dirParseLoc = "$saveDir/suphpfixDirStore.txt"; if ( $path eq "" || !defined($path) ) { print color 'red'; print "ERROR: Was unable to find the WWW_Root for $cpUser !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'red'; exit(1); } if ( ! -e "$path") { print color 'red'; print "ERROR: WWW_Root $path for $cpUser doesn't exist !\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Generating Directory list for $path ... \n"; system("find $path -type d > $dirParseLoc"); &savePermOwner("$dirParseLoc","dirs","$cpUser"); } sub parseAndRestore { print color 'red'; die "parseAndRestore() requires at least one argument. Arg1 (required) RestoreType (values: all or singleAccnt). Arg2 (required only if arg1 is singleAccnt) specific cPanel user to restore. \n" if (scalar(@_) < 1); print color 'reset'; my $restoreType = $_[0]; my $accntToRestore = $_[1]; my $passedArg3 = $_[2]; my ($jsonFile, $jsonDir); my $json = new JSON; if ( $restoreType eq "singleAccnt" ) { my $fileStoreLocation = "/root/datastore_suphpfix/datastore_file_suphpfix.$accntToRestore.JSON"; my $dirStoreLocation = "/root/datastore_suphpfix/datastore_dir_suphpfix.$accntToRestore.JSON"; if ( ! -e $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $accntToRestore doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( ! -e $dirStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $accntToRestore doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $accntToRestore exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $dirStoreLocation ) { print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $accntToRestore exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; exit(1); } $jsonFile = `cat $fileStoreLocation`; $jsonDir = `cat $dirStoreLocation`; my ($jsonFileToParse, $jsonDirToParse); eval{ $jsonFileToParse = $json->allow_nonref->utf8->relaxed->decode($jsonFile); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; eval{ $jsonDirToParse = $json->allow_nonref->utf8->relaxed->decode($jsonDir); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; my (@fileArray,@cpuserFileArray,@permFileArray,@ownerFileArray,@groupFileArray); my (@dirArray,@cpuserDirArray,@permDirArray,@ownerDirArray,@groupDirArray); ###FILE for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { push(@cpuserFileArray, $cpuser); }; for my $file( @{$jsonFileToParse->{acct}->{file}} ) { push(@fileArray, $file); }; for my $perm( @{$jsonFileToParse->{acct}->{perm}} ) { push(@permFileArray, $perm); }; for my $owner( @{$jsonFileToParse->{acct}->{owner}} ) { push(@ownerFileArray, $owner); }; for my $group( @{$jsonFileToParse->{acct}->{group}} ) { push(@groupFileArray, $group); }; ###DIR for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { push(@cpuserDirArray, $cpuser); }; for my $file( @{$jsonDirToParse->{acct}->{file}} ) { push(@dirArray, $file); }; for my $perm( @{$jsonDirToParse->{acct}->{perm}} ) { push(@permDirArray, $perm); }; for my $owner( @{$jsonDirToParse->{acct}->{owner}} ) { push(@ownerDirArray, $owner); }; for my $group( @{$jsonDirToParse->{acct}->{group}} ) { push(@groupDirArray, $group); }; print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting suPHP fixes for $accntToRestore\n"; print color 'reset'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting file permissions/ownerships for $accntToRestore ...\n"; my $fileSlot = "0"; for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{FILE} file number: $fileSlot for $cpuser...\n"; #Deal with spaces and special chars @fileArray[$fileSlot] =~ s/ /\\ /g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; #@fileArray[$fileSlot] =~ s/[\^]/\\^/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "chmod @permFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "chown @ownerFileArray[$fileSlot].@groupFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { if ( -e @fileArray[$fileSlot] ) { if ( is_immutable(@fileArray[$fileSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @fileArray[$fileSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @fileArray[$fileSlot] =~ s/ /\\ /g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; system("chmod @permFileArray[$fileSlot] @fileArray[$fileSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chmod command: chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chown command: chown @ownerFileArray[$fileSlot].@groupFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } } elsif ( ! -e @fileArray[$fileSlot] ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @fileArray[$fileSlot] no longer exists, skipping... \n"; print color 'reset'; } } $fileSlot++; }; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting directory permissions/ownerships for $accntToRestore ... \n"; my $dirSlot = "0"; for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{DIR} directory number: $dirSlot for $cpuser...\n"; #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "chmod @permDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "chown @ownerDirArray[$dirSlot].@groupDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { if ( -e @dirArray[$dirSlot] ) { if ( is_immutable(@dirArray[$dirSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; system("chmod @permDirArray[$dirSlot] @dirArray[$dirSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chmod command: chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chown command: chown @ownerDirArray[$dirSlot].@groupDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } if ( $dirSlot == 0 ) { print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Uncommenting php tweaks for $accntToRestore ...\n"; system("find @dirArray[$dirSlot] -name .htaccess -exec sed -i 's/^#php_/php_/g' {} \\\;"); } } } elsif ( ! -e @dirArray[$dirSlot] ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] no longer exists, skipping... \n"; print color 'reset'; } } $dirSlot++; }; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "suPHP fixes reverted for $accntToRestore\n"; print color 'reset'; } elsif ( $restoreType eq "all" ) { print color 'red'; open FD, "/root/datastore_suphpfix/backedupUserList.all" or die "ERROR: Could not open datastore file (/root/datastore_suphpfix/backedupUserList.all): $! . For more details see --help\n"; my @backedUpCPusers = ; close FD; system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print color 'reset'; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; exit(1); } my $hash = `cat /root/.accesshash`; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $restoreCounter = "0"; for my $user ( @backedUpCPusers ) { chomp($user); print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting suPHP fixes for $user ... \n"; print color 'reset'; ### Let's make sure the cPanel account to be restored still exists on the server. my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); my $validUser; for my $userCheck( @{$accntList->{acct}} ) { if ( $userCheck->{user} eq $user ) { $validUser = "true"; } }; if ( $validUser ne "true" ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: $user is no longer a valid cPanel user on this system. You can either take another snapshot of all accounts, or you can restore snapshots for backed up cPanel accounts that are still on the server individually with --restore-state cPanelUser. For more details, see --help \n"; print color 'reset'; exit(1); } my $fileStoreLocation = "/root/datastore_suphpfix/datastore_file_suphpfix.$user.JSON"; my $dirStoreLocation = "/root/datastore_suphpfix/datastore_dir_suphpfix.$user.JSON"; if ( ! -e $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $user doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( ! -e $dirStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $user doesn't exist. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $fileStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) for $user exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot.\n"; print color 'reset'; exit(1); } if ( -z $dirStoreLocation ) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) for $user exists but is empty. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); } $jsonFile = `cat $fileStoreLocation`; $jsonDir = `cat $dirStoreLocation`; my ($jsonFileToParse, $jsonDirToParse); eval{ $jsonFileToParse = $json->allow_nonref->utf8->relaxed->decode($jsonFile); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($fileStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; eval{ $jsonDirToParse = $json->allow_nonref->utf8->relaxed->decode($jsonDir); }; if ($@) { print color 'red'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "ERROR: Datastore file ($dirStoreLocation) is not valid JSON. Please take another snapshot or restore the datastore files from a previous snapshot. \n"; print color 'reset'; exit(1); }; my (@fileArray,@cpuserFileArray,@permFileArray,@ownerFileArray,@groupFileArray); my (@dirArray,@cpuserDirArray,@permDirArray,@ownerDirArray,@groupDirArray); ###FILE for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { push(@cpuserFileArray, $cpuser); }; for my $file( @{$jsonFileToParse->{acct}->{file}} ) { push(@fileArray, $file); }; for my $perm( @{$jsonFileToParse->{acct}->{perm}} ) { push(@permFileArray, $perm); }; for my $owner( @{$jsonFileToParse->{acct}->{owner}} ) { push(@ownerFileArray, $owner); }; for my $group( @{$jsonFileToParse->{acct}->{group}} ) { push(@groupFileArray, $group); }; ###DIR for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { push(@cpuserDirArray, $cpuser); }; for my $file( @{$jsonDirToParse->{acct}->{file}} ) { push(@dirArray, $file); }; for my $perm( @{$jsonDirToParse->{acct}->{perm}} ) { push(@permDirArray, $perm); }; for my $owner( @{$jsonDirToParse->{acct}->{owner}} ) { push(@ownerDirArray, $owner); }; for my $group( @{$jsonDirToParse->{acct}->{group}} ) { push(@groupDirArray, $group); }; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting file permissions/ownerships for $user ... \n"; my $fileSlot = "0"; for my $cpuser( @{$jsonFileToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{FILE} file number: $fileSlot for $cpuser...\n"; #Deal with spaces and special chars @fileArray[$fileSlot] =~ s/ /\\ /g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; print color 'green'; print "{FILE} Proposed chmod: "; print color 'red'; print "chmod @permFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{FILE} Proposed chown: "; print color 'red'; print "chown @ownerFileArray[$fileSlot].@groupFileArray[$fileSlot] @fileArray[$fileSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { if ( -e @fileArray[$fileSlot] ) { if ( is_immutable(@fileArray[$fileSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] ","WARNING: @fileArray[$fileSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @fileArray[$fileSlot] =~ s/ /\\ /g; @fileArray[$fileSlot] =~ s/'/\\'/g; @fileArray[$fileSlot] =~ s/&/\\&/g; @fileArray[$fileSlot] =~ s/[(]/\\(/g; @fileArray[$fileSlot] =~ s/[)]/\\)/g; #@fileArray[$fileSlot] =~ s/[\^]/\\^/g; @fileArray[$fileSlot] =~ s/[|]/\\|/g; @fileArray[$fileSlot] =~ s/"/\\"/g; @fileArray[$fileSlot] =~ s/!/\\!/g; @fileArray[$fileSlot] =~ s//\\>/g; @fileArray[$fileSlot] =~ s/`/\\`/g; system("chmod @permFileArray[$fileSlot] @fileArray[$fileSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chmod command: chmod @permFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("chown @ownerFileArray[$fileSlot]:@groupFileArray[$fileSlot] @fileArray[$fileSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chown command: chown @ownerFileArray[$fileSlot].@groupFileArray[$fileSlot] @fileArray[$fileSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } } elsif ( ! -e @fileArray[$fileSlot] ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] ", "WARNING: @fileArray[$fileSlot] no longer exists, skipping... \n"; print color 'reset'; } } $fileSlot++; }; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting directory permissions/ownerships for $user ... \n"; my $dirSlot = "0"; for my $cpuser( @{$jsonDirToParse->{acct}->{cpanelUser}} ) { if ( $passedArg3 eq "verify" ) { print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; print "{DIR} directory number: $dirSlot for $cpuser...\n"; #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; print color 'green'; print "{DIR} Proposed chmod: "; print color 'red'; print "chmod @permDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'green'; print "{DIR} Proposed chown: "; print color 'red'; print "chown @ownerDirArray[$dirSlot].@groupDirArray[$dirSlot] @dirArray[$dirSlot]"; print color 'reset'; print "\n"; print color 'yellow'; print "-----------------------------------------------\n"; print color 'reset'; } else { if ( -e @dirArray[$dirSlot] ) { if ( is_immutable(@dirArray[$dirSlot]) ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] is immutable, skipping...\n"; print color 'reset'; } else { #Deal with spaces and special chars @dirArray[$dirSlot] =~ s/ /\\ /g; @dirArray[$dirSlot] =~ s/'/\\'/g; @dirArray[$dirSlot] =~ s/&/\\&/g; @dirArray[$dirSlot] =~ s/[(]/\\(/g; @dirArray[$dirSlot] =~ s/[)]/\\)/g; @dirArray[$dirSlot] =~ s/[|]/\\|/g; @dirArray[$dirSlot] =~ s/"/\\"/g; @dirArray[$dirSlot] =~ s/!/\\!/g; @dirArray[$dirSlot] =~ s//\\>/g; @dirArray[$dirSlot] =~ s/`/\\`/g; system("chmod @permDirArray[$dirSlot] @dirArray[$dirSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chmod command: chmod @permDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("chown @ownerDirArray[$dirSlot]:@groupDirArray[$dirSlot] @dirArray[$dirSlot]"); if ( $? != 0 ) { print color 'red'; print "Error in chown command: chown @ownerDirArray[$dirSlot].@groupDirArray[$dirSlot] @dirArray[$dirSlot] \n"; print "Got Error: $!\n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } if ( $dirSlot == 0 ) { print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Uncommenting php tweaks for $user ...\n"; system("find @dirArray[$dirSlot] -name .htaccess -exec sed -i 's/^#php_/php_/g' {} \\\;"); } } } elsif ( ! -e @dirArray[$dirSlot] ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "WARNING: @dirArray[$dirSlot] no longer exists, skipping... \n"; print color 'reset'; } } $dirSlot++; }; $restoreCounter++; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "* Task complete.\n"; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] ","suPHP fixes reverted for $user Completed: $restoreCounter/", scalar(@backedUpCPusers), "\n"; print color 'reset'; }; print color 'green'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "suPHP fixes reverted for $restoreCounter accounts. \n"; print color 'reset'; exit; } } sub restorePermsOwners { die "restorePermsOwners() requires one argument. Arg1: cpanelUser or 'all' Optional arg2: verify\n" if (scalar(@_) < 1); my $passedArg = $_[0]; my $passedArg2 = $_[1]; if ( $passedArg eq "all" ) { if ( $passedArg2 eq "verify" ) { &parseAndRestore("all","no","$passedArg2"); } else { &parseAndRestore("all"); } } else { #Verify if passedArg is a valid cpanel user system("QUERY_STRING=\\\"regen=1\\\" /usr/local/cpanel/whostmgr/bin/whostmgr ./setrhash &> /dev/null"); my $hashfile = '/root/.accesshash'; if (! -e $hashfile) { print color 'red'; print "ERROR: Failed to automatically generate hash! Please try logging into WHM and click `Setup Remote Access Key` and then re-run this script. \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } my $hash = `cat /root/.accesshash`; $hash =~ s/\n//g; my $auth = "WHM root:" . $hash; my $json = new JSON; my $ua = LWP::UserAgent->new; $ua->agent("suphpfix"); $ua->env_proxy(); my $request = HTTP::Request->new(POST => "http://127.0.0.1:2086/json-api/listaccts"); $request->header( Authorization => $auth ); $request->content_type('application/jsonrequest'); $request->content("{}"); #create and ensure UTF8 my $response = $ua->request($request); my $listRaw = $response->content; my $list = decode("UTF-8", $listRaw); my $accntList = $json->allow_nonref->utf8->relaxed->decode($list); our @cpUsersArray; my $isValid; for my $userCnt( @{$accntList->{acct}} ) { if ( $userCnt->{user} eq $passedArg ) { $isValid = "true"; if ( $passedArg2 eq "verify" ) { &parseAndRestore("singleAccnt","$passedArg","$passedArg2"); } else { &parseAndRestore("singleAccnt","$passedArg"); } } } if ( $isValid ne "true" ) { print color 'red'; print "ERROR: $passedArg is not a valid cPanel user.\n"; print color 'reset'; exit(1); } } } sub preChecks { if ( $< != 0 ) { print color 'reset'; print color 'red'; print "ERROR: must be root to run this.\n"; print color 'reset'; exit(1); } my $cpanelVerify = `/sbin/chkconfig --list | grep cpanel`; if ( $cpanelVerify !~/3:on/) { print color 'reset'; print color 'red'; print "ERROR: cPanel not found (needs to be enabled in chkconfig run level 3).\n"; print color 'reset'; exit(1); } system("which find &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: find not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("which chmod &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: chmod not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } system("which chown &> /dev/null"); if ( $? != 0 ) { print color 'red'; print "ERROR: chown not found in path! \n"; print "An unexpected error has occurred please email ssullivan\@liquidweb.com \n"; print color 'reset'; exit(1); } } ################ #### MAIN() #### ################ &preChecks(); print "-suphpfix $version\n"; if ( $ARGV[0] eq "--prep" ) { if ( $ARGV[1] eq "all" ) { &suphpfixMain("all"); } elsif ( defined($ARGV[1]) ) { &suphpfixMain("$ARGV[1]"); } else { print "Please give a valid argument to --prep. For help, see --help.\n"; } } elsif ( $ARGV[0] eq "--save-state" ) { if ( defined($ARGV[1]) ) { if ( $ARGV[1] eq "all" ) { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Saving state for all cPanel accounts...\n"; print color 'reset'; &getCPuserDetails("all"); } else { &getCPuserDetails("$ARGV[1]"); } } elsif ( !defined($ARGV[1]) ) { print "Please pass a valid option to --save-state . For help, see --help \n"; exit(1); } } elsif ( $ARGV[0] eq "--restore-state" ) { if ( defined($ARGV[1]) ) { if ( $ARGV[1] eq "all" ) { if ( defined($ARGV[2]) ) { if ( $ARGV[2] eq "verify" ) { print color 'yellow'; print "\nRunning restore state in verify mode. No changes will be made!\n\n"; print color 'reset'; sleep(3); &restorePermsOwners("all","verify"); } } else { print color 'yellow'; print "[ ",POSIX::strftime("%m/%d/%Y %H:%M:%S ", localtime) ,"] " , "Reverting suPHP fixes for all saved accounts ... \n"; print color 'reset'; &restorePermsOwners("all"); } } else { if ( $ARGV[2] eq "verify" ) { print color 'yellow'; print "\nRunning restore state in verify mode. No changes will be made!\n\n"; print color 'reset'; sleep(3); &restorePermsOwners("$ARGV[1]","verify"); } else { &restorePermsOwners("$ARGV[1]"); } } } elsif ( !defined($ARGV[1]) ) { print "Please pass a valid option to --restore-state . For help, see --help \n"; exit(1); } } elsif ( $ARGV[0] eq "--help" ) { &help(); } elsif ( $ARGV[0] eq "" ) { print color 'yellow'; print "NOTICE: suphpfix requires an option. For help, see --help\n"; print color 'reset'; } else { print "Option '$ARGV[0]' not recognized. For help, see --help \n"; }