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(); if (isset($result->asset_id) && !empty($result->asset_id)) { $assetid = $result->asset_id; } else { debug_log("No ASSET ID assigned .... "); return 'NO_ASSETID'; } // 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'; $statusOpt = $this->addlMsg; } 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); if (file_exists(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE)) { unlink(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); } if (file_exists(trailingslashit( BACKUP_DIR ))) { $this->utiObj->actifend_rrmdir( BACKUP_DIR ); } // Get the file from Actifend-cloud storage $this->actifend_restore_back($params_array, $file_attribute); // Unzip the backup file if(file_exists(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE)) { @mkdir(BACKUP_DIR); $extDone = $this->utiObj->pclExtractZipData(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, BACKUP_DIR); if ($extDone != 0) { debug_log("Used PclZip extract to restore."); } else { debug_log("PclZip extract to restore failed."); if ($this->zip_check()) { $this->utiObj->actifend_unzip(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, BACKUP_DIR); debug_log(BACKUP_FILE . " file unzipped with ZIP extension!"); } else { update_option('ActifendRestoreStatus', 'failed'); return; } } // delete backup zip file unlink(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); // if uploads folder is NOT in the backup do not delete original folder $backup_uploads_dir = trailingslashit( BACKUP_DIR ) . trailingslashit( 'wp-content' ) . 'uploads'; if (! file_exists($backup_uploads_dir)) { array_push($this->wpcontent2skip, 'uploads'); } } else { update_option('ActifendRestoreStatus', 'failed'); $this->addlMsg = 'Backup file does not exist.'; debug_log($this->addlMsg); return; } if (strcmp($backup_type, 'full') === 0) { // 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') === 1) { debug_log("Insufficient privileges to restore fully."); debug_log("Exiting after unzipping the backup file to wp-content/wp_backup folder!"); } else { $this->actifend_restore_core(); debug_log("core files restored!"); } }elseif (strcmp($backup_type, 'partial') === 0) { // restore database $this->actifend_restore_database(); debug_log("WordPress database restored!"); } // delete the backup folder $this->utiObj->actifend_rrmdir(BACKUP_DIR); debug_log("wp_backup folder deleted"); // take a new baseline of the file structure $this->fiObj->createBaseline(); 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 { $locations = array( trailingslashit( BACKUP_DIR ) . 'wp-content' => WP_CONTENT_DIR, trailingslashit( BACKUP_DIR ) . 'wp-admin' => ADMIN_DIR, trailingslashit( BACKUP_DIR ) . 'wp-includes' => INCLUDES_DIR, trailingslashit( BACKUP_DIR ) => ABSPATH ); // if plugins and themes are in a custom location (other then default wp-content) if (! is_link(trailingslashit( WP_CONTENT_DIR ) . 'plugins') && dirname(WP_PLUGIN_DIR) !== WP_CONTENT_DIR) { debug_log("Restoring plugins to custom location ..."); $locations[trailingslashit( BACKUP_DIR ) . trailingslashit('wp-content') . 'plugins'] = WP_PLUGIN_DIR; } if (! is_link(trailingslashit( WP_CONTENT_DIR ) . 'themes') && dirname(THEMES_DIR) !== WP_CONTENT_DIR) { debug_log("Restoring themes to custom location ..."); $locations[trailingslashit( BACKUP_DIR ) . trailingslashit('wp-content') . 'themes'] = THEMES_DIR; } if (! is_link(trailingslashit( WP_CONTENT_DIR ) . 'uploads') && dirname(UPLOADS_DIR) !== WP_CONTENT_DIR) { debug_log("Restoring uploads to custom location ..."); $locations[trailingslashit( BACKUP_DIR ) . trailingslashit('wp-content') . 'uploads'] = UPLOADS_DIR; } foreach ($locations as $source => $destination) { if ($destination == ABSPATH) { // Restore other core files that go into 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. $ret = $this->actifend_restore_other_core(); if (!$ret) { debug_log("$destination restore failed. Admin needs to manually restore them. "); } } else { $ret = $this->actifend_restore_folder($source, $destination); if (!$ret) { debug_log("$destination restore failed. Admin needs to manually restore them. "); } } debug_log("$destination folder added to backup."); } debug_log("actifend_restore_core restore done!"); } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendRestoreStatus', $e->getMessage()); return false; } } /** * actifend_restore_folder * Restores the source folder to its destination * @param source * @param destination * @return boolean (true or false) */ public function actifend_restore_folder($source, $destination) { try { if (!empty($source)) { debug_log("Folder to Restore: ". $source); if ($destination == WP_CONTENT_DIR) { $ret = $this->utiObj->actifend_rrmdir_wp_content($destination, $this->wpcontent2skip); } else { $ret = $this->utiObj->actifend_rrmdir($destination); } if (! $ret) { debug_log($destination . " removal, before restore, failed!"); return false; } else { debug_log("Deleted folder " . $destination); // copy the source folder to the destination if (!$this->cp_chmod_folder($source, $destination)) { debug_log("$destination folder restoration failed."); return false; } else { debug_log("$destination folder restored fully."); return true; } } } else { debug_log("$source folder is empty!"); return true; } } catch (Exception $e) { return false; } } /** * cp_chmod_folder * This function copies files and then change permissions on the files. */ private function cp_chmod_folder($source, $destination) { if (!empty($source)) { if (!$this->copy_folder($source, $destination)) { return false; // v1.4 - do not try to change permissions at all; // if the folder is writable, which would be, copy should suffice. } return true; } 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; } } } ?>