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();
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_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'];
$this->max_uploads_size = intval($response['Result']['max_uploads_size']);
// 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 (strcmp($backup_type, 'full') === 0) {
// create the backup zip file
$this->actifend_wp_backup();
} else {
$backup_type = 'content';
// create the backup zip file
$this->actifend_partial_backup();
}
// remove the directory used to keep the backup content directory.
$this->utiObj->actifend_rrmdir(BACKUP_DIR);
// 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");
$resp_status = $this->actifend_storage_asset_check($uri, $az_headers);
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" => $backup_type,
"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" => $backup_type,
"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 {
$error_msg = 'Storage Lookup Error: ' . $response['response']['message'];
debug_log($error_msg);
update_option('ActifendBackupStatus', $error_msg);
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
$contentLength = $end_byte - $start_byte + 1;
$contents = fread($handle, $contentLength);
// initiate the api call
$addl_headers = array("x-ms-write" => "update",
"x-ms-range" => "bytes=" . $start_byte . "-" . $end_byte,
"Content-Length" => (string) $contentLength);
$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_storage_asset_check
* @az_uri actifend storage url
* @az_headers request header information
* @return response status code
* @access private
*/
public function actifend_storage_asset_check($az_uri, $az_headers) {
$response = wp_remote_head($az_uri, array('headers' => $az_headers));
$resp_status = $response['response']['code'];
// v1.4.4.3 Retry if the check fails first time
if ($resp_status !== 404 || $resp_status !== 200) {
$response = wp_remote_head($az_uri, array('headers' => $az_headers));
$resp_status = $response['response']['code'];
}
return $resp_status;
}
/**
* actifend_partial_backup
* Takes partial backup and creates a zip file
*/
public function actifend_partial_backup() {
try {
// Add uploads folder upto a limit`specified
$uploads_size = $this->utiObj->get_size_of_folder( UPLOADS_DIR );
if ($uploads_size < $this->max_uploads_size
|| $this->max_uploads_size !== 0) {
@mkdir( BACKUP_DIR );
@mkdir( trailingslashit( BACKUP_DIR ) . 'wp-content');
$uploads_dest = trailingslashit( BACKUP_DIR ) . trailingslashit( 'wp-content' ) . 'uploads';
@mkdir($uploads_dest);
$this->copy_directory( UPLOADS_DIR, $uploads_dest );
debug_log( UPLOADS_DIR . " folder added to backup ... ");
}
// database access credentials.
$dbcreds = 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 ) . trailingslashit( 'wp-db' ), //where to backup
'db_exclude_tables' => array() //tables to exclude
);
// use function to backup the sql.
$res = $this->__backup_mysql_database($dbcreds);
if ($res) {
debug_log("Database dump completed successfully.");
} else {
debug_log("ERROR: Database dump failed.");
update_option('ActifendBackupStatus', 'DB dump failed.');
return false;
}
// // copy the wp-content/uploads in wp_backup directory
// $destination = trailingslashit( BACKUP_DIR ) . "wp-content/uploads";
// $this->copy_directory(UPLOADS_DIR, $destination);
// 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("Pcl zip compression is used!");
update_option('ActifendBackupStatus', 'Compresion done with PclZip.');
return true;
}else {
debug_log("pcl zip compression failed.");
// chek if zip extension is enabled
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("DB backup Zipping complete!");
update_option('ActifendBackupStatus', 'Compresion done with Zip extension.');
return true;
} else {
update_option('ActifendBackupStatus', 'PclZip compression failed. No Zip extension.');
return false;
}
}
} catch (Exception $e) {
update_option('ActifendBackupStatus', $e->getMessage());
return false;
}
}
/**
* 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 {
$locations = array( WP_CONTENT_DIR => trailingslashit( BACKUP_DIR ) . 'wp-content',
ADMIN_DIR => trailingslashit( BACKUP_DIR ) . 'wp-admin',
INCLUDES_DIR => trailingslashit( BACKUP_DIR ) . 'wp-includes',
ABSPATH => BACKUP_DIR );
// 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
&& ! in_array(dirname(WP_PLUGIN_DIR), array_keys($locations))) {
debug_log("Backing up plugins from custom location ...");
$locations[WP_PLUGIN_DIR] = trailingslashit( BACKUP_DIR ) . trailingslashit('wp-content') . 'plugins';
}
if (! is_link(trailingslashit( WP_CONTENT_DIR ) . 'themes')
&& dirname(THEMES_DIR) !== WP_CONTENT_DIR
&& ! in_array(dirname(THEMES_DIR), array_keys($locations))) {
debug_log("Backing up themes from custom location ...");
$locations[THEMES_DIR] = trailingslashit( BACKUP_DIR ) . trailingslashit('wp-content') . 'themes';
}
// Add uploads folder upto a limit`specified
$uploads_size = $this->utiObj->get_size_of_folder( UPLOADS_DIR );
if ($uploads_size > $this->max_uploads_size
&& $this->max_uploads_size !== 0) {
array_push($this->wpcontent2skip, 'uploads');
debug_log("Skipping uploads folder backup ... ");
}
@mkdir(BACKUP_DIR);
foreach ($locations as $source => $destination) {
if (strcmp($source, ABSPATH) === 0) {
// recursive should be false
$this->copy_directory($source, $destination, false);
} else {
$this->copy_directory($source, $destination);
}
debug_log("$source folder added to backup.");
}
// 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 ) . trailingslashit('wp-db'), //where to backup
'db_exclude_tables' => array() //tables to exclude
);
// use function to backup the sql.
$res = $this->__backup_mysql_database($para);
if ($res) {
debug_log("Database dump completed successfully.");
} else {
debug_log("ERROR: Database dump failed.");
}
// 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("PclZip compression DONE!");
return true;
}else {
debug_log("pcl zip compression failed.");
if ($this->zip_check()) {
debug_log("Checking with ZIP extension ... ");
$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 {
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, $recursive=true) {
try {
if ($source != BACKUP_DIR) {
$directory = opendir($source);
@mkdir($destination);
while (false !== ($file = readdir($directory))) {
if (($file != '.') && ($file != '..')) {
if (is_dir(trailingslashit($source) . $file) && $recursive === true) {
if (! in_array($file, $this->wpcontent2skip)) {
$this->copy_directory(trailingslashit($source) . $file,
trailingslashit($destination) . $file);
}
} else {
@$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext != 'log' && $file != BACKUP_FILE) {
@copy(trailingslashit($source) . $file,
trailingslashit($destination) . $file);
}
}
}
}
closedir($directory);
return True;
}
} catch (Exception $e) {
debug_log($e->getMessage());
update_option('ActifendBackupStatus', $e->getMessage());
return False;
}
}
/**
* __backup_mysql_database (using mysqldump method)
* @param sql filename
* @return void
*/
private function __backup_mysql_database($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;
}
}
?>