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_backup_process * After completion of upload to Actifend-cloud status is to be sent * Actifend BE */ public function actifend_backup_process() { // get asset id $result = $this->utiObj->getActifendInfo(); $assetid = $result->asset_id; // Execute Backup process $this->actifend_wordpress_backup($assetid); $statusOpt = get_option('ActifendBackupStatus'); // check if no requests are pending exit without doing anything if ($statusOpt == 'None') exit(0); // Send the status to Actifend BE if ($statusOpt == 'complete') { $backupStatus = 'done'; $statusOpt = 'Backup to Actifend-cloud successful.'; } else { $backupStatus = 'failed'; } $actifend_array = array('status' => $backupStatus, '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 . "/wpbackup"; $this->utiObj->actifend_postViaCurl($actifend_url, $actifend_params, "PATCH"); debug_log("Status update sent to Actifend BE."); } /** * actifend_wordpress_backup * * This function create the receive the backup request after that create * the zip of all your wordpress files and database as per the request full * backup and the at send those files to the azure stroage in * chunks of 3MB and then send the done status after completion of all steps. * * @return null */ public function actifend_wordpress_backup($asset_id) { try { debug_log("Check for backup requests initiated ..."); // check if any backup requests are waiting at BE $response = $this->utiObj->getViaCurl(ACTIFEND_BACKUP_END_POINT, $asset_id, 'wpbackup'); if (isset($response) && !empty($response) && !is_wp_error($response)) { if (($response['ResponseCode'] == '2000') && ($response['Message'] == 'success')) { // It create the backup of the files of the wordpress// $assetid = $response['Result']['asset_id']; $account_name = $response['Result']['stor_name']; $share_name = $response['Result']['share_name']; $sas_token = $response['Result']['sas_token']; $this->req_id = $response['Result']['_id']; $backup_type = $response['Result']['backup_type']; // suspend the crons until backup & restore is completed $this->utiObj->reset_actifend_crons('remove'); // option setting update_option('ActifendBackupStatus', 'initiated'); $file = BACKUP_FILE; // defined in actifendConstants $max_range = 3 * 1024 * 1024; if ($backup_type == 'full') { // create the backup zip file $this->actifend_wp_backup(); $dir_path = BACKUP_DIR; // defined in actifendConstants // remove the directory used to keep the backup content directory. $this->utiObj->actifend_rrmdir($dir_path); } // List the files and the directory ---------------------------------- // check if the folder with assetid as name exists $uri = 'https://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "?" . $sas_token . "&restype=directory"; $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)); $resp_status = $response['response']['code']; if ($resp_status === 404) { debug_log("$assetid Folder does not exist!"); // create the folder! $uri = 'http://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "?" . $sas_token . "&restype=directory"; $az_headers = array_merge($az_headers, array('Content-Length' => '0')); $response = wp_remote_request($uri, array('headers' => $az_headers, 'method' => 'PUT')); if ($response['response']['code'] == 201) { debug_log("$assetid Folder created on Actifend-cloud!"); } // Now create a sub folder $subfolder = date("Y-m-d_G_i_s"); $uri = 'http://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "/" . $subfolder . "?" . $sas_token . "&restype=directory"; $addl_headers = array("x-ms-meta-bkp_type" => "full", "x-ms-meta-category" => "backup"); $args = array_merge($az_headers, $addl_headers); $response = wp_remote_request($uri, array('headers' => $args, 'method' => 'PUT')); if ($response['response']['code'] === 201) { debug_log("Subfolder " . $subfolder . " with metadata created. Now move on and place the file there ...
"); } } elseif ($resp_status === 200) { debug_log("Folder " . $assetid . " exists! move on and look for the subfolder...
"); // Build the subfolder name // create the subfolder as it would not be existing!! $subfolder = date("Y-m-d_G_i_s"); $uri = 'http://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "/" . $subfolder . "?" . $sas_token . "&restype=directory"; $addl_headers = array("x-ms-meta-bkp_type" => "full", "x-ms-meta-category" => "backup", "Content-length" => '0'); $args = array_merge($az_headers, $addl_headers); $response = wp_remote_request($uri, array('headers' => $args, 'method' => 'PUT')); if ($response['response']['code'] === 201) { debug_log("Subfolder " . $subfolder . " with metadata created. Now move on and place the file there ...
"); } else { update_option('ActifendBackupStatus', 'Unable to create the Subfolder in Actifend-cloud.'); exit("Unable to create the folder, exiting!"); } } else { debug_log("
%s
", $response['response']); update_option('ActifendBackupStatus', $response['response']); return; } // Call Azure API to save the file in the storage. $fsize = filesize(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); $uri = 'https://' . $account_name . '.file.core.windows.net/' . $share_name . "/" . $assetid . "/" . $subfolder . "/" . $file . "?" . $sas_token; $today = gmdate("D, d M Y G:i:s T"); $addl_headers = array('Content-Length' => '0', 'x-ms-type' => 'file', 'x-ms-content-length' => (string) $fsize); $heads = array_merge($az_headers, $addl_headers); $response = wp_remote_request($uri, array('headers' => $heads, 'method' => 'PUT')); if (is_wp_error($response) || ($response['response']['code'] == 400)) { debug_log("Starting file upload to Actifend-cloud failed!"); update_option('ActifendBackupStatus', 'File upload to Actifend-cloud incomplete!'); return; } $handle = fopen(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, "rb"); $uri_range = $uri . '&comp=range'; // For an update operation, the range can be up to 4 MB in size. // file needs to be uploaded in chunks of $max_range (defined above) if ($fsize > $max_range) { $mod = $fsize / $max_range; $iters = round($mod, 0); if ($iters < $mod) { $iters += 1; } } else { $iters = 1; } $start_byte = 0; $ie = 0; for ($i = 1; $i <= $iters; $i++) { // set the start and end byte values for the range $end_byte = $start_byte + $max_range - 1; if ($i == $iters) { $end_byte = $fsize - 1; } // Read the contents in the file $contents = fread($handle, ($end_byte - $start_byte + 1)); // initiate the api call $addl_headers = array("x-ms-write" => "update", "x-ms-range" => "bytes=" . $start_byte . "-" . $end_byte); $heads = array_merge($az_headers, $addl_headers); $response = wp_remote_request($uri_range, array('headers' => $heads, 'body' => $contents, 'method' => 'PUT')); if (is_wp_error($response) || ($response['response']['code'] !== 201)) { debug_log("Upload to Actifend-cloud failed!"); debug_log($response); update_option('ActifendBackupStatus', $response); } else { $start_byte = $end_byte + 1; $ie = $i; } } if ($ie != $iters) { debug_log("File upload incomplete! :("); update_option('ActifendBackupStatus', 'File upload to Actifend-cloud incomplete!'); } else { debug_log("File upload to Azure storage completed!
"); // delete backup zip file unlink(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); update_option('ActifendBackupStatus', 'complete'); // reinitate the cron for backup & restore $this->utiObj->reset_actifend_crons('add'); } }else { debug_log("No backup requests pending!"); update_option('ActifendBackupStatus', 'None'); return; } }elseif(is_wp_error($response)) { debug_log($response->get_error_message()); update_option('ActifendBackupStatus', $response->get_error_message()); return; } } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendBackupStatus', $e->getMessage()); return; } } /** * Actifend Wp Backup * * This function copy the all wordpress files and create a .aql file of wordpress database and add those to wp_backup directory.Create a zip file in the wp-content * with wp_backup.zip file. * * @return null * * @access private */ private function actifend_wp_backup() { try { // $source_path = ABSPATH . "wp-config.php"; $dir_path = BACKUP_DIR; mkdir($dir_path); // copy the wp-content in wp_backup direcory $destination = trailingslashit( BACKUP_DIR ) . "wp-content"; $this->copy_directory(WP_CONTENT_DIR, $destination); // copy content of the wp-admin in the backup temp directory. $destination_wp_admin = trailingslashit( BACKUP_DIR ) . "wp-admin"; $this->copy_directory(ADMIN_DIR, $destination_wp_admin); // copy the content of the wp-include $destination_wp_include = trailingslashit( BACKUP_DIR ) . "wp-includes"; $this->copy_directory(INCLUDES_DIR, $destination_wp_include); // copy the files that in wordpress main directory $this->actifend_wprdpress_directory_files(); // if plugins and themes are in a custom location (other then default wp-content) if (strpos(WP_PLUGIN_DIR, 'wp-content') === false) { debug_log("Backing up plugins from custom location ..."); $source_plugins = WP_PLUGIN_DIR; $dest_plugins = trailingslashit( BACKUP_DIR ) . "wp-content/plugins"; $this->copy_directory($source_plugins, $dest_plugins); } if (strpos(THEMES_DIR, 'wp-content') === false) { debug_log("Backing up themes from custom location ..."); $source_themes = THEMES_DIR; $dest_themes = trailingslashit( BACKUP_DIR ) . "wp-content/themes"; $this->copy_directory($source_themes, $dest_themes); } // database access credentials. $para = array( 'db_host' => DB_HOST, //mysql host 'db_uname' => DB_USER, //user 'db_password' => DB_PASSWORD, //pass 'db_to_backup' => DB_NAME, //database name 'db_backup_path' => trailingslashit( BACKUP_DIR ) . 'wp-db/', //where to backup 'db_exclude_tables' => array() //tables to exclude ); // use function to backup the sql. $res = $this->__backup_mysql_database_v2($para); if ($res) { debug_log("Database dump completed successfully."); } else { debug_log("ERROR: Database dump failed."); } if ($this->zip_check()) { $this->utiObj->zipData(trailingslashit( BACKUP_DIR ), trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); if (file_exists(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE)) { chmod(trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE, 0777); } debug_log("Site backup Zipping complete!"); return True; }else { // send files with pcl zip compression using PclZip $zipDone = $this->utiObj->pclZipData(trailingslashit( BACKUP_DIR ), trailingslashit( WP_CONTENT_DIR ) . BACKUP_FILE); if ($zipDone != 0) { debug_log("No zip extension. pcl zip is used!"); return True; }else { debug_log("pcl zip compression failed."); update_option('ActifendBackupStatus', 'PclZip compression failed.'); return False; } } } catch (Exception $e) { update_option('ActifendBackupStatus', $e->getMessage()); return False; } } /** * copy directory * * 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_directory($source, $destination) { try { if ($source != BACKUP_DIR) { $directory = opendir($source); @mkdir($destination); while (false !== ($file = readdir($directory))) { if (($file != '.') && ($file != '..')) { if (is_dir($source . '/' . $file)) { if (!in_array($file, $this->wpcontent2skip)) { $this->copy_directory($source . '/' . $file, $destination . '/' . $file); } } else { @$ext = pathinfo($file, PATHINFO_EXTENSION); if ($ext != 'log') { if ($file != BACKUP_FILE) { copy($source . '/' . $file, $destination . '/' . $file); } } } } } closedir($directory); return True; } } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendBackupStatus', $e->getMessage()); return False; } } /** * actifend_wprdpress_directory_files * * This function is used to copy only files not the direcory of wordpreses directory for examplae wp-config.php, wp-login.php etc. * * @access private * * @return null. */ private function actifend_wprdpress_directory_files() { try { $dir = ABSPATH; // Open a directory, and read its contents if (is_dir($dir)) { if ($dh = opendir($dir)) { while (($data = readdir($dh)) !== false) { $file = ABSPATH . $data; if (file_exists($file) && !is_dir($file)) { //echo "filename:" . $file . "
"; //$destination = ABSPATH . "wp-content/wp_backup/" . $data; $destination = trailingslashit( BACKUP_DIR ) . $data; @copy($file, $destination); } } closedir($dh); return True; } } } catch (Exception $e) { update_option('ActifendBackupStatus', $e->getMessage()); return False; } } /** * __backup_mysql_database * * bachup Database function used to create the .sql file for the wordpress database this file used in the wp_backup.zip file. * * @param array $param these are database credentials. * * @return null * */ private function __backup_mysql_database($params) { try { // take a new baseline of the file structure $this->fiObj->createBaseline(); global $wpdb; $mtables = array(); $contents = "-- Database: `" . $params['db_to_backup'] . "` --\n"; $mysqli = new mysqli($params['db_host'], $params['db_uname'], $params['db_password'], $params['db_to_backup']); if ($mysqli->connect_error) { die('Error : (' . $mysqli->connect_errno . ') ' . $mysqli->connect_error); } $results = $mysqli->query("SHOW TABLES"); while ($row = $results->fetch_array()) { if (!in_array($row[0], $params['db_exclude_tables'])) { $mtables[] = $row[0]; } } // identify the charset of the db $charset = $wpdb->charset; debug_log("Wordpress DB Charset: $charset"); $contents .= "SET NAMES $charset;\n"; $contents .= "SET time_zone = '+00:00';\n"; $contents .= "SET foreign_key_checks = 0;\n"; $contents .= "SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';\n"; foreach ($mtables as $table) { $contents .= "-- Table `" . $table . "` --\n"; $contents .= "DROP TABLE IF EXISTS `" . $table . "`;\n"; $results = $mysqli->query("SHOW CREATE TABLE " . $table); while ($row = $results->fetch_array()) { $contents .= $row[1] . ";\n\n"; } $results = $mysqli->query("SELECT * FROM " . $table); $row_count = $results->num_rows; $fields = $results->fetch_fields(); $fields_count = count($fields); $insert_head = "INSERT INTO `" . $table . "` ("; for ($i = 0; $i < $fields_count; $i++) { $insert_head .= "`" . $fields[$i]->name . "`"; if ($i < $fields_count - 1) { $insert_head .= ', '; } } $insert_head .= ")"; $insert_head .= " VALUES\n"; if ($row_count > 0) { $r = 0; while ($row = $results->fetch_array()) { if (($r % 400) == 0) { $contents .= $insert_head; } $contents .= "("; for ($i = 0; $i < $fields_count; $i++) { $row_content = str_replace("\n", "\\n", $mysqli->real_escape_string($row[$i])); switch ($fields[$i]->type) { case 8: case 3: $contents .= $row_content; break; default: $contents .= "'" . $row_content . "'"; } if ($i < $fields_count - 1) { $contents .= ', '; } } if (($r + 1) == $row_count || ($r % 400) == 399) { $contents .= ");\n\n"; } else { $contents .= "),\n"; } $r++; } } } if (!is_dir($params['db_backup_path'])) { mkdir($params['db_backup_path'], 0777, true); } $dir = $params['db_backup_path']; $backup_file_name = $dir . "actifend-db-backup.sql"; $fp = fopen($backup_file_name, 'w+'); if ($result = fwrite($fp, $contents)) { debug_log("DB Backup file created '--$backup_file_name' ($result)"); }else { debug_log("DB Backup file creation failed!"); update_option('ActifendBackupStatus', 'DB Backup file creation failed!'); return False; } fclose($fp); unset($fiObj); unset($utiObj); debug_log("Wordpress database backup complete!"); return True; } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendBackupStatus', $e->getMessage()); return False; } } /** * __backup_mysql_database_v2 (using mysqldump method) * @param sql filename * @return void */ private function __backup_mysql_database_v2($params) { try { $this->fiObj->createBaseline(); if (!is_dir($params['db_backup_path'])) { mkdir($params['db_backup_path'], 0777, true); } $dir = trailingslashit( BACKUP_DIR ) . 'wp-db/'; $backup_file_name = $dir . "actifend-db-backup.sql"; $cmd = "mysqldump"; $host = explode(':', DB_HOST); $port = strpos(DB_HOST, ':') ? end($host) : ''; $host = reset($host); // We don't want to create a new DB $cmd .= ' --no-create-db'; // Allow lock-tables to be overridden $cmd .= ' --single-transaction'; // Make sure binary data is exported properly $cmd .= ' --hex-blob'; // Username $cmd .= ' -u ' . escapeshellarg(DB_USER); // Don't pass the password if it's blank if (DB_PASSWORD) $cmd .= ' -p' . escapeshellarg(DB_PASSWORD); // Set the host $cmd .= ' -h ' . escapeshellarg($host); // Set the port if it was set if (!empty($port) && is_numeric($port)) $cmd .= ' -P ' . $port; // The file we're saving too $cmd .= ' -r ' . escapeshellarg($backup_file_name); // Exclude tables if any (default is empty array) $wp_db_exclude_table = array(); if (!empty($wp_db_exclude_table)) { foreach ($wp_db_exclude_table as $wp_db_exclude_table) { $cmd .= ' --ignore-table=' . DB_NAME . '.' . $wp_db_exclude_table; } } // The database we're dumping $cmd .= ' ' . escapeshellarg(DB_NAME); // Pipe STDERR to STDOUT $cmd .= ' 2>&1'; // Store any returned data in an error $stderr = shell_exec($cmd); // Skip the new password warning that is output in mysql > 5.6 if (trim($stderr) === 'Warning: Using a password on the command line interface can be insecure.') { $stderr = ''; } debug_log("mysqldump: SQL Dump completed. Verification to be done."); return $this->verify_mysqldump($backup_file_name); } catch (Exception $e) { debug_log($e->getMessage()); update_option('ActifendBackupStatus', $e->getMessage()); return False; } } public function verify_mysqldump($SQLfilename) { // If we've already passed then no need to check again if (!empty($this->mysqldump_verified)) return true; // If we have an empty file delete it if (@filesize($SQLfilename) === 0) unlink($SQLfilename); // If the file still exists then it must be good if (file_exists($SQLfilename)) return $this->mysqldump_verified = true; return false; } } ?>