utiObj = new Utility; $this->fiObj = new ActifendFileIntegrity( ABSPATH ); } /** * Zip extension check */ public function zip_check() { if (extension_loaded('zip')) { return True; } return false; } /** * Zip extension and OS check */ public function zip_and_os_check() { if (extension_loaded('zip') || (strtoupper(substr(PHP_OS, 0, 3)) !== "WIN")) { return True; } return false; } /** * actifend_restore_process * After completion of restore to Actifend-cloud status is to be sent * Actifend BE */ public function actifend_restore_process() { // get asset id $result = $this->utiObj->getActifendInfo(); $assetid = $result->asset_id; // Execute Backup process $this->actifend_total_restore($assetid); $statusOpt = get_option('ActifendRestoreStatus'); // check if no requests are pending exit without doing anything if ($statusOpt == 'None') exit(0); // Send the status to Actifend BE if ($statusOpt == 'complete') { $restoreStatus = 'done'; $statusOpt = 'Restore of Backup from Actifend-cloud completed.'; } else { $restoreStatus = 'failed'; } if (get_option('FilePermissionProblem') != 0) { $statusOpt .= " Insufficient file privileges to restore fully. Additional manual restore needed."; } $actifend_array = array('status' => $restoreStatus, 'zip_enabled' => $this->zip_check(), 'reason' => $statusOpt, 'reqid' => $this->req_id); $actifend_params = json_encode($actifend_array); // send status done status by curl usind the PATCH method when backup send to azure successfully. $actifend_url = ACTIFEND_BACKUP_END_POINT . $assetid . "/wprestore"; $this->utiObj->actifend_postViaCurl($actifend_url, $actifend_params, "PATCH"); debug_log("Status update sent to Actifend BE."); } /** * actifend_total_restore * * This function is used to restore the all files from azure and then replace the backup files with the server wordpress files which we backed up. In case * of full backup file then database also replced. */ public function actifend_total_restore($asset_id) { try { debug_log("Check for restore requests initiated ..."); // check if any restore requests are waiting to be executed $response = $this->utiObj->getViaCurl(ACTIFEND_RESTORE_END_POINT, $asset_id, 'wprestore'); debug_log("Response: " . $response['ResponseCode']); if (!empty($response) && isset($response)) { if (($response['Message'] == 'success') && ($response['ResponseCode'] == '2000')) { // suspend the crons until restore is completed $this->utiObj->reset_actifend_crons('remove'); $backup_type = $response['Result']['backup_type']; debug_log("Backup type: " . $backup_type); $params_array = array('assetid' => $response['Result']['asset_id'], 'account_name' => $response['Result']['stor_name'], 'share_name' => $response['Result']['share_name'], 'sub_folder' => $response['Result']['backup_name'], 'sas_token' => $response['Result']['sas_token']); $this->req_id = $response['Result']['_id']; $file_attribute = $this->get_azure_file_name($params_array); // Restore $this->actifend_restore_back($params_array, $file_attribute); if ($backup_type == 'full') { if(file_exists(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE)){ if ($this->zip_check()) { $this->utiObj->actifend_unzip(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, BACKUP_DIR); debug_log(BACKUP_FILE . " file unzipped!"); }else { @mkdir(BACKUP_DIR); $extDone = $this->utiObj->pclExtractZipData(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, BACKUP_DIR); if ($extDone != 0) { debug_log("No ZIP extension. Used PclZip extract to restore."); }else { debug_log("PclZip extract to restore failed."); exit(0); } } // delete backup zip file unlink(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); // restore database $this->actifend_restore_database(); debug_log("WordPress database restored!"); // Exit here if not enough privileges $this->utiObj->check_file_privileges(); if (get_option('FilePrivilegesInsufficient') != 0) { debug_log("Insufficient privileges to restore fully."); debug_log("Exiting after unzipping the backup file to wp-content/wp_backup folder!"); update_option('ActifendRestoreStatus', 'complete'); return; } $this->actifend_restore_core(); debug_log("core files restored!"); // take a new baseline of the file structure $this->fiObj->createBaseline(); $this->utiObj->actifend_rrmdir(BACKUP_DIR); debug_log("wp_backup folder deleted"); } else { debug_log(BACKUP_FILE . " file does not exist!"); } } update_option('ActifendRestoreStatus', 'complete'); debug_log("actifend_total_restore done!"); // reinitiate the crons $this->utiObj->reset_actifend_crons('add'); }else { debug_log("No restore requests pending."); update_option("ActifendRestoreStatus", 'None'); return; } } } catch (Exception $e) { debug_log($e->getMessage()); update_option("ActifendRestoreStatus", $e->getMessage()); return false; } } /** * get_azure_file_name * * This function is used to get the file name and other attributes of the backup file stored in azure storage whcih are require to get the file from the azure stroage. * @param array $params_array Array contain list parameters required to access the azure storage. e.g (token, account name and asset_id etc) * @return array $file_attribute Array contain the file name and size. */ private function get_azure_file_name($params_array) { try { $assetid = $params_array['assetid']; $account_name = $params_array['account_name']; $share_name = $params_array['share_name']; $sas_token = $params_array['sas_token']; $subfolder = $params_array['sub_folder']; //sub_folder // $uri = 'https://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "/" . $subfolder . "?" . $sas_token . "&restype=directory&comp=list"; $uri = 'https://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "/" . $subfolder . "/" . BACKUP_FILE . "?" . $sas_token; $today = gmdate("D, d M Y G:i:s T"); $az_headers = array("x-ms-date" => $today, "x-ms-version" => "2015-04-05"); $response = wp_remote_head($uri, array('headers' => $az_headers)); $fsize = $response['headers']['content-length']; debug_log("File size: " . $fsize); $file_attribute = array('fname' => BACKUP_FILE, 'fsize' => $fsize); return $file_attribute; } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } /** * actifend_restore_back * * This function is used to get the latest backup file from the azure storage and save the file in wp-content directory in zip format. * * @param array $params_array This array contain the token and other information that required to access the storage account. * @param array $file_attribute This array contain the latest file file attribute that required to downlod the backup file. */ private function actifend_restore_back($params_array, $file_attribute) { try { debug_log("Executing actifend_restore_back function ..."); $fp = fopen(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, 'wb'); $fsize = $file_attribute['fsize']; $fname = $file_attribute['fname']; debug_log("Filename to restore: " . $fname); $subfolder = $params_array['sub_folder']; debug_log("Backup to restore: " . $subfolder); $max_range = 3 * 1024 * 1024; $assetid = $params_array['assetid']; $account_name = $params_array['account_name']; $share_name = $params_array['share_name']; $sas_token = $params_array['sas_token']; $today = gmdate("D, d M Y G:i:s T"); $uri = 'https://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "/" . $subfolder . "/" . $fname . "?" . $sas_token; if ($fsize > $max_range) { $heads = array("x-ms-range" => 'bytes=0-' . ($max_range - 1), "x-ms-version" => "2015-04-05", "x-ms-date" => $today); $response = wp_remote_get($uri, array('headers' => $heads)); $content = wp_remote_retrieve_body($response); } else { $heads = array("x-ms-range" => 'bytes=0-' . ($fsize - 1), "x-ms-version" => "2015-04-05", "x-ms-date" => $today); $response = wp_remote_get($uri, array('headers' => $heads)); $content = wp_remote_retrieve_body($response); } $fw = fwrite($fp, $content); $remain_size = $fsize - $max_range; $start_range = $max_range; while ($remain_size > $max_range) { $end_range = $start_range + $max_range; $heads = array("x-ms-range" => 'bytes=' . $start_range . '-' . ($end_range - 1), "x-ms-version" => "2015-04-05", "x-ms-date" => $today); $response = wp_remote_get($uri, array('headers' => $heads)); $content = wp_remote_retrieve_body($response); $fw = fwrite($fp, $content); $start_range = $end_range; $remain_size = $fsize - $end_range; if (($remain_size < $max_range) && ($fsize > $end_range)) { $heads = array("x-ms-range" => 'bytes=' . ($end_range) . '-' . ($fsize - 1), "x-ms-version" => "2015-04-05", "x-ms-date" => $today); $response = wp_remote_get($uri, array('headers' => $heads)); $content = wp_remote_retrieve_body($response); $fw = fwrite($fp, $content); } } fclose($fp); if (!file_exists(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE)) { $errors = error_get_last(); debug_log($errors['message'] . " error while writing file wp_backup.zip"); update_option('ActifendRestoreStatus', $errors['message']); return; } if (!chmod(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, 0777)) { debug_log("Could not change the permissions on wp_backup.zip file."); } debug_log(BACKUP_FILE . " retrieved from Azure storage and written to local disk."); debug_log("Local file size: " . filesize(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE) . " bytes"); } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } /** * actifend_restore_core * * This method is used to replace the wp-content ,wp-admin, wp-include and other files with the wordpress backup files that we downloaded from the azure storage. * @param null * @return null */ private function actifend_restore_core() { try { //wp-admin restore $source = trailingslashit( BACKUP_DIR ) . "wp-admin"; debug_log("Starting actifend_restore_core function"); if (!empty($source)) { debug_log("ADMIN_DIR: " . ADMIN_DIR); debug_log("copying wp-admin files."); if (!$this->utiObj->actifend_rrmdir(ADMIN_DIR)) { debug_log("Could not delete the " . ADMIN_DIR . " folder!"); } else { debug_log(ADMIN_DIR . " folder deleted recursively!"); // Restore files if(!$this->cp_chmod_folder($source, ADMIN_DIR)) { debug_log('cp_chmod_folder of wp-admin failed.'); } else { debug_log("wp-admin files restored fully."); } } } else { debug_log("wp-admin source folder is empty!"); return false; } //wp-includes restore //============================== $source = trailingslashit( BACKUP_DIR ) . "wp-includes"; if (!empty($source)) { //$destination = ABSPATH . "wp-includes"; debug_log("INCLUDES_DIR: " . INCLUDES_DIR); debug_log("copying wp-includes files."); if (!$this->utiObj->actifend_rrmdir(INCLUDES_DIR)) { debug_log("Could not delete the " . INCLUDES_DIR . " folder!"); } else { debug_log(INCLUDES_DIR . " folder deleted recursively!"); // Restore files $this->cp_chmod_folder($source, INCLUDES_DIR); debug_log("wp-includes files restored fully."); } } else { debug_log("wp-includes source folder is empty!"); } /** Restore other core files that go into WP_HOME_DIR * Actifend attempts to restore these files from backup but the process will fail, * if won't have the correct ownership. * It is typical for the files to be owned by the FTP account that originally uploaded them. */ $other_core_cp_res = $this->actifend_restore_other_core(); if (!$other_core_cp_res) { $this->addlMsg = "WP Core files restore failed. Admin needs to manually restore them."; debug_log($this->addlMsg); } else { debug_log("WP Core files restored to WP Home directory successfully!"); } // wp-content restore //============================== $source = trailingslashit( BACKUP_DIR ) . "wp-content"; if (!empty($source)) { debug_log("WP_CONTENT_DIR: ". WP_CONTENT_DIR); if (! $this->utiObj->actifend_rrmdir_wp_content(WP_CONTENT_DIR)) { debug_log(WP_CONTENT_DIR . " removal, before restore, failed!"); debug_log("Skipping copy of wp-content folder ..."); } else { debug_log("Selected folders from " . WP_CONTENT_DIR . " deleted recursively!"); // check if plugins, themes are in custom or default folders // typical shared hosting installations will have them in custom folders // if (dirname(WP_PLUGIN_DIR) != WP_CONTENT_DIR ) { if (!$this->cp_chmod_folder($source, WP_CONTENT_DIR)) { debug_log("wp-content folder restoration failed."); } else { debug_log("wp-content folder restored fully."); } // } else { // if (!$this->cp_chmod_folder($source, WP_CONTENT_DIR)) { // debug_log("plugins folder restoration failed."); // } else { // debug_log("plugins folder restored fully."); // } // } } } else { debug_log("wp-content source folder is empty!"); } debug_log("actifend_restore_core restore done!"); } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } /** * cp_chmod_folder * This function copies files and then change permissions on the files. */ private function cp_chmod_folder($source, $destination) { debug_log("Attempting to restore the " . $destination . " folder..."); if (!empty($source)) { // changed from copy_core_files to copy_folder if (!$this->copy_folder($source, $destination)) { debug_log($destination . " folder copy failed!"); return false; // v1.4 - do not try to change permissions at all; // if the folder is writable, which would be, copy should suffice. // } else { // debug_log($source . " files copied to " . $destination . " successfully."); // if (!$this->utiObj->chmod_r($destination)) { // debug_log("Permission changes to " . $destination . " failed."); // $this->addlMsg = 'Permissions denied to copy files to destination folders!'; // $this->addlMsg .= ' Please try again after giving suitable permissions!'; // return false; // } } debug_log($destination . " folder restored!"); return true; } else { debug_log($source . " folder is empty!"); return false; } } /** * copy_core_files * * This is fucntion simple copy the content of one direcory to the other directory. * * @param string $source This parameter contain the source directory path. * @param string $destination This parameter is used for the destination directory. */ private function copy_core_files($source, $destination) { try { $directory = opendir($source); if (!mkdir($destination, 0775)) { debug_log("Failed to create folder " . $destination); } while (false !== ($file = readdir($directory))) { if (($file != '.') && ($file != '..')) { if (is_dir($source . '/' . $file)) { $this->copy_core_files($source . '/' . $file, $destination . '/' . $file); } else { @$ext = pathinfo($file, PATHINFO_EXTENSION); if ($ext != 'log') { if (!copy($source . '/' . $file, $destination . '/' . $file)) { debug_log("Could NOT copy " . $source . " file to " . $destination); return false; } } } } } closedir($directory); return true; } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } /** * copy folder * * Copy directory function is used to copy the full content of one directory to another directory. * Skips folders that are mentioned in the class array variable wpcontent2skip * Skips files with extension .log and file defined as BACKUP_FILE constant that are * inside the source folder * * @param string $source Source directory path. * @param string $destination Destination path where the content of source directory copied. */ private function copy_folder($source, $destination) { try { if ($source != BACKUP_DIR) { $directory = opendir($source); if (is_link($destination)) @mkdir(readlink($destination)); else @mkdir($destination); while (false !== ($file = readdir($directory))) { if (($file != '.') && ($file != '..')) { if (is_dir($source . '/' . $file)) { $this->copy_folder($source . '/' . $file, $destination . '/' . $file); } else { @$ext = pathinfo($file, PATHINFO_EXTENSION); if ($ext != 'log') { if ($file != BACKUP_FILE) { if(!copy($source . '/' . $file, $destination . '/' . $file)) { debug_log('Failed to copy $file to $destination'); } } } } } } closedir($directory); return true; } } catch (Exception $e) { update_option('ActifendRestoreStatus', $e->getMessage()); debug_log($e->getMessage()); return false; } } /** * actifend_restore_other_core * * This is function restore file of wordpress directory like wp-login and wp-config etc. */ private function actifend_restore_other_core() { try { $dir = ABSPATH; $result = false; // Open a directory, and read its contents if (is_dir($dir)) { if ($dh = opendir($dir)) { while (($data = readdir($dh)) !== false) { $dest_file = ABSPATH . $data; if (!is_dir($dest_file)) { // if (!chmod($dest_file, 0775)) { // debug_log("Could not change permissions on " . $dest_file); // $result = false; // } if (!unlink($dest_file)) { debug_log("Could not delete file " . $dest_file); $result = false; } $source_file = trailingslashit( BACKUP_DIR ) . $data; if (file_exists($source_file)) { if (!copy($source_file, $dest_file)) { $errors = error_get_last(); debug_log($errors['message'] . " error while copying file " . $source_file); $result = false; } else { debug_log("Copied " . $source_file . " to " . $dest_file); $result = true; } } } } closedir($dh); } } return $result; debug_log("actifend_restore_other_core function executed!"); } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } /** * actifend_restore_database * * Function used to restore the database from the backup file. */ private function actifend_restore_database() { try { $filename = trailingslashit( BACKUP_DIR ) . 'wp-db/actifend-db-backup.sql'; if (file_exists($filename)) { // MySQL host $mysql_host = DB_HOST; // MySQL username $mysql_username = DB_USER; // MySQL password $mysql_password = DB_PASSWORD; // Database name $mysql_database = DB_NAME; // Connect to MySQL server $con = mysqli_connect($mysql_host, $mysql_username, $mysql_password); if (!$con) { debug_log("ERROR: Unable to connect to MySQL instance!"); debug_log("Debugging error: " . mysqli_connect_error() . PHP_EOL); } else { debug_log("Connected to MySQL instance!"); } // Select database if (!mysqli_select_db($con, $mysql_database)) { debug_log("ERROR: Unable to select ". $mysql_database); } else { debug_log("Selected WP mysql database!"); } // Temporary variable, used to store current query $templine = ''; // Read in entire file $lines = file($filename); // Loop through each line foreach ($lines as $line) { // Skip it if it's a comment if (substr($line, 0, 2) == '--' || $line == '') { continue; } // Add this line to the current segment $templine .= $line; // If it has a semicolon at the end, it's the end of the query if (substr(trim($line), -1, 1) == ';') { // Perform the query if (!mysqli_query($con, $templine)) { // debug_log('Error performing query ' . $templine . ": " . mysqli_error($con)); debug_log('Error performing $templine query!'); } // Reset temp variable to empty $templine = ''; } } } debug_log("actifend_restore_database function executed!"); } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } } ?>