// Last backup has not finished yet or timed out.
    if (time() - $currentBackup['updated_time'] > 60 * $timeoutMinutes) {
        // Most likely timed out.
    } else {
        // Still chugging along possibly.
        $currentBackupStats['isRunning'] = '1';
    }
}
$currentBackupStats['processStarted'] = $currentBackup['start_time'];
$currentBackupStats['processFinished'] = $currentBackup['finish_time'];
$currentBackupStats['processStepTitle'] = '';
$currentBackupStats['processStepFunction'] = '';
$currentBackupStats['processStepElapsed'] = 0;
foreach ((array) $currentBackup['steps'] as $step) {
    if ('0' == $step['finish_time']) {
        $currentBackupStats['processStepTitle'] = backupbuddy_core::prettyFunctionTitle($step['function']);
        $currentBackupStats['processStepFunction'] = $step['function'];
        $currentBackupStats['processStepElapsed'] = microtime(true) - $step['start_time'];
        break;
    }
}
$currentBackupStats['backupType'] = $currentBackup['type'];
$currentBackupStats['profileTitle'] = htmlentities($currentBackup['profile']['title']);
$currentBackupStats['scheduleTitle'] = $currentBackup['schedule_title'];
if (@file_exists($currentBackup['archive_file'])) {
    $currentBackupStats['archiveFile'] = basename($currentBackup['archive_file']);
} else {
    $currentBackupStats['archiveFile'] = '';
}
$currentBackupStats['archiveURL'] = '';
if (isset($currentBackup['archive_url'])) {
 function process_backup($serial, $trigger = 'manual')
 {
     //pb_backupbuddy::status( 'details', 'Running process_backup() for serial `' . $serial . '`.' );
     // Assign reference to backup data structure for this backup.
     if (!isset($this->_backup_options)) {
         pb_backupbuddy::status('details', 'About to load fileoptions data.');
         $attempt_transient_prefix = 'pb_backupbuddy_lock_attempts-';
         require_once pb_backupbuddy::plugin_path() . '/classes/fileoptions.php';
         pb_backupbuddy::status('details', 'Fileoptions instance #39.');
         $this->_backup_options = new pb_backupbuddy_fileoptions(backupbuddy_core::getLogDirectory() . 'fileoptions/' . $serial . '.txt');
         if (true !== ($result = $this->_backup_options->is_ok())) {
             // Unable to access fileoptions.
             $attempt_delay_base = 10;
             // Base number of seconds to delay. Each subsequent attempt increases this delay by a multiple of the attempt number.
             $max_attempts = 8;
             // Max number of attempts to try to delay around a file lock. Delay increases each time.
             $this->_backup['serial'] = $serial;
             // Needs to be populated for use by cron schedule step.
             pb_backupbuddy::status('warning', __('Warning #9034 B. Unable to access fileoptions data.', 'it-l10n-backupbuddy') . ' Warning: ' . $result, $serial);
             // Track lock attempts in transient system. This is not vital & since locks may be having issues track this elsewhere.
             $lock_attempts = get_transient($attempt_transient_prefix . $serial);
             if (false === $lock_attempts) {
                 $lock_attempts = 0;
             }
             $lock_attempts++;
             set_transient($attempt_transient_prefix . $serial, $lock_attempts, 60 * 60 * 24);
             // Increment lock attempts. Hold attempt count for 24 hours to help make sure we don't lose attempt count if very low site activity, etc.
             if ($lock_attempts > $max_attempts) {
                 pb_backupbuddy::status('error', 'Backup halted. Maximum number of attempts made attempting to access locked fileoptions file. This may be caused by something causing backup steps to run out of order or file permission issues on the temporary directory holding the file `' . $fileoptions_file . '`. Verify correct permissions.', $serial);
                 pb_backupbuddy::status('haltScript', '', $serial);
                 // Halt JS on page.
                 delete_transient($attempt_transient_prefix . $serial);
                 return false;
             }
             $wait_time = $attempt_delay_base * $lock_attempts;
             pb_backupbuddy::status('message', 'A scheduled step attempted to run before the previous step completed. The previous step may have failed or two steps may be attempting to run simultaneously.', $serial);
             pb_backupbuddy::status('message', 'Waiting `' . $wait_time . '` seconds before continuing. Attempt #' . $lock_attempts . ' of ' . $max_attempts . ' max allowed before giving up.', $serial);
             $this->cron_next_step(false, $wait_time);
             return false;
         } else {
             // Accessed fileoptions. Clear/reset any attempt count.
             delete_transient($attempt_transient_prefix . $serial);
         }
         pb_backupbuddy::status('details', 'Fileoptions data loaded.');
         $this->_backup =& $this->_backup_options->options;
     }
     if ($this->_backup_options->options['profile']['backup_mode'] != '1') {
         // Only check for cronPass action if in modern mode.
         pb_backupbuddy::status('finishAction', 'cronPass');
     }
     // Handle cancelled backups (stop button).
     if (true == get_transient('pb_backupbuddy_stop_backup-' . $serial)) {
         // Backup flagged for stoppage. Proceed directly to cleanup.
         pb_backupbuddy::status('message', 'Backup STOPPED. Post backup cleanup step has been scheduled to clean up any temporary files.');
         foreach ($this->_backup['steps'] as $step_id => $step) {
             if ($step['function'] != 'post_backup') {
                 if ($step['start_time'] == 0) {
                     $this->_backup['steps'][$step_id]['start_time'] = -1;
                     // Flag for skipping.
                 }
             } else {
                 // Post backup step.
                 $this->_backup['steps'][$step_id]['args'] = array(true, true);
                 // Run post_backup in fail mode & delete backup file.
             }
         }
         //pb_backupbuddy::save();
         $this->_backup_options->save();
         pb_backupbuddy::status('haltScript', '');
         // Halt JS on page.
     }
     $found_next_step = false;
     // Loop through steps finding first step that has not run.
     foreach ((array) $this->_backup['steps'] as $step_index => $step) {
         $this->_currentStepIndex = $step_index;
         //pb_backupbuddy::status( 'details', 'step: ' . $step['function'] . 'start: ' . $step['start_time'] );
         if ($step['start_time'] != -1 && $step['start_time'] != 0 && $step['finish_time'] == 0) {
             // A step is not marked for skippage, has begun but has not finished. This should not happen but the WP cron is funky. Wait a while before continuing.
             // Re-load, respecting locks to help avoid race conditions.
             $this->_backup_options->load($ignore_lock = false, $create_file = false, $retryCount = 0);
             if (true !== $this->_backup_options->is_ok()) {
                 // Unable to access fileoptions.
                 pb_backupbuddy::status('warning', 'Unable to update out of order step attempt count due to file lock. It may be being written to by the other step at this moment.');
             } else {
                 pb_backupbuddy::status('details', 'Saving update to step attempt count.');
                 $this->_backup['steps'][$step_index]['attempts']++;
                 // Increment this as an attempt.
                 $this->_backup_options->save();
             }
             if ($step['attempts'] < 6) {
                 $wait_time = 60 * $step['attempts'];
                 // Each attempt adds a minute of wait time.
                 pb_backupbuddy::status('warning', 'A scheduled step attempted to run before the previous step completed. Waiting `' . $wait_time . '` seconds before continuing for it to catch up. Attempt number `' . $step['attempts'] . '`.');
                 $this->cron_next_step(false, $wait_time);
                 return false;
             } else {
                 // Too many attempts to run this step.
                 pb_backupbuddy::status('error', 'A scheduled step attempted to run before the previous step completed. After several attempts (`' . $step['attempts'] . '`) of failure BackupBuddy has given up. Halting backup.');
                 return false;
             }
             break;
         } elseif ($step['start_time'] == 0) {
             // Step that is not marked for skippage and has not started yet.
             $found_next_step = true;
             $this->_backup['steps'][$step_index]['start_time'] = time();
             // Set this step time to now.
             $this->_backup['steps'][$step_index]['attempts']++;
             // Increment this as an attempt.
             $this->_backup_options->save();
             pb_backupbuddy::status('details', 'Found next step to run: `' . $step['function'] . '`.');
             break;
             // Break out of foreach loop to continue.
         } elseif ($step['start_time'] == -1) {
             // Step flagged for skipping. Do not run.
             pb_backupbuddy::status('details', 'Step `' . $step['function'] . '` flagged for skipping. Skipping.');
         } else {
             // Last case: Finished. Skip.
             // Do nothing for completed steps.
             //pb_backupbuddy::status( 'details', 'Step `' . $step['function'] . '` doing nothing with start `' . $step['start_time'] . '`.' );
         }
     }
     // End foreach().
     if ($found_next_step === false) {
         // No more steps to perform; return.
         pb_backupbuddy::status('details', 'No more steps found.');
         return false;
     }
     //pb_backupbuddy::save();
     pb_backupbuddy::status('details', __('Peak memory usage', 'it-l10n-backupbuddy') . ': ' . round(memory_get_peak_usage() / 1048576, 3) . ' MB');
     /********* Begin Running Step Function **********/
     if (method_exists($this, $step['function'])) {
         /*
         $args = '';
         foreach( $step['args'] as $arg ) {
         	if ( is_array( $arg ) ) {
         		$args .= '{' . implode( ',', $arg ) . '},';
         	} else {
         		$args .= str_replace('],[', '|', trim(json_encode($step['args']), '[]'));
         	}
         }
         */
         pb_backupbuddy::status('details', '-----');
         pb_backupbuddy::status('details', 'Starting step function `' . $step['function'] . '`. Attempt #' . ($step['attempts'] + 1) . '.');
         // attempts 0-indexed.
         $functionTitle = $step['function'];
         $subFunctionTitle = '';
         $functionTitle = backupbuddy_core::prettyFunctionTitle($step['function'], $step['args']);
         pb_backupbuddy::status('startFunction', json_encode(array('function' => $step['function'], 'title' => $functionTitle)));
         if ('' != $subFunctionTitle) {
             pb_backupbuddy::status('startSubFunction', json_encode(array('function' => $step['function'] . '_subfunctiontitle', 'title' => $subFunctionTitle)));
         }
         $response = call_user_func_array(array(&$this, $step['function']), $step['args']);
     } else {
         pb_backupbuddy::status('error', __('Error #82783745: Invalid function `' . $step['function'] . '`'));
         $response = false;
     }
     /********* End Running Step Function **********/
     //unset( $step );
     if ($response === false) {
         // Function finished but reported failure.
         // Failure caused by backup cancellation.
         if (true == get_transient('pb_backupbuddy_stop_backup-' . $serial)) {
             pb_backupbuddy::status('haltScript', '');
             // Halt JS on page.
             return false;
         }
         pb_backupbuddy::status('error', 'Failed function `' . $this->_backup['steps'][$step_index]['function'] . '`. Backup terminated.');
         pb_backupbuddy::status('errorFunction', $this->_backup['steps'][$step_index]['function']);
         pb_backupbuddy::status('details', __('Peak memory usage', 'it-l10n-backupbuddy') . ': ' . round(memory_get_peak_usage() / 1048576, 3) . ' MB');
         pb_backupbuddy::status('haltScript', '');
         // Halt JS on page.
         $args = print_r($this->_backup['steps'][$step_index]['args'], true);
         $attachment = NULL;
         $attachment_note = 'Enable full logging for troubleshooting (a log will be sent with future error emails while enabled).';
         if (pb_backupbuddy::$options['log_level'] == '3') {
             // Full logging enabled.
             // Log file will be attached.
             $log_file = backupbuddy_core::getLogDirectory() . 'status-' . $serial . '_' . pb_backupbuddy::$options['log_serial'] . '.txt';
             if (file_exists($log_file)) {
                 $attachment = $log_file;
                 $attachment_note = 'A log file is attached which may provide further details.';
             } else {
                 $attachment = NULL;
             }
         }
         // Send error notification email.
         backupbuddy_core::mail_error('One or more backup steps reported a failure. ' . $attachment_note . ' Backup failure running function `' . $this->_backup['steps'][$step_index]['function'] . '` with the arguments `' . $args . '` with backup serial `' . $serial . '`. Please run a manual backup of the same type to verify backups are working properly or view the backup status log.', NULL, $attachment);
         return false;
     } else {
         // Function finished successfully.
         $this->_backup['steps'][$step_index]['finish_time'] = time();
         if ('backup_create_database_dump' != $this->_backup['steps'][$step_index]['function']) {
             // Wipe arguments except for DB backup which tracks tables dumps. Keeps fileoptions for growing crazily for finished steps containing state data such as deployment or new zip functionality passing chunking state.
             $this->_backup['steps'][$step_index]['args'] = time();
         }
         $this->_backup['updated_time'] = time();
         $this->_backup_options->save();
         pb_backupbuddy::status('details', sprintf(__('Finished function `%s`. Peak memory usage', 'it-l10n-backupbuddy') . ': ' . round(memory_get_peak_usage() / 1048576, 3) . ' MB', $this->_backup['steps'][$step_index]['function']) . ' with BackupBuddy v' . pb_backupbuddy::settings('version')) . '.';
         pb_backupbuddy::status('finishFunction', json_encode(array('function' => $this->_backup['steps'][$step_index]['function'])));
         pb_backupbuddy::status('details', '-----');
         $found_another_step = false;
         foreach ($this->_backup['steps'] as $next_step) {
             // Loop through each step and see if any have not started yet.
             if ($next_step['start_time'] == 0) {
                 // Another unstarted step exists. Schedule it.
                 $found_another_step = true;
                 if ($this->_backup['profile']['backup_mode'] == '2') {
                     // Modern mode with crons.
                     $this->cron_next_step();
                 } else {
                     // classic mode
                     $this->process_backup($this->_backup['serial'], $trigger);
                 }
                 break;
             }
         }
         // End foreach().
         if ($found_another_step == false) {
             pb_backupbuddy::status('details', __('No more backup steps remain. Finishing...', 'it-l10n-backupbuddy'));
             $this->_backup['finish_time'] = time();
             $this->_backup_options->save();
             pb_backupbuddy::status('startFunction', json_encode(array('function' => 'backup_success', 'title' => __('Backup completed successfully.', 'it-l10n-backupbuddy'))));
             pb_backupbuddy::status('finishFunction', json_encode(array('function' => 'backup_success')));
         } else {
             pb_backupbuddy::status('details', 'Completed step function `' . $step['function'] . '`.');
             //pb_backupbuddy::status( 'details', 'The next should run in a moment. If it does not please check for plugin conflicts and that the next step is scheduled in the cron on the Server Information page.' );
         }
         // If full logging, output fileoptions state data to brwoser for display in console.
         if (pb_backupbuddy::$options['log_level'] == '3') {
             // Full logging enabled.
             //if ( defined( 'BACKUPBUDDY_DEV' ) && ( true === BACKUPBUDDY_DEV ) ) {
             $thisBackup = $this->_backup;
             if ('' != $this->_backup['deployment_direction']) {
                 // Remove steps for deployment because it gets too large.
                 $thisBackup['steps'] = '** Removed since deployment type **';
             }
             pb_backupbuddy::status('backupState', json_encode($thisBackup));
             //base64_encode( json_encode( $this->_backup ) ) );
         }
         return true;
     }
 }
 public function run($arguments)
 {
     $timeoutMinutes = 5;
     // Minutes after which BackupBuddy assumed a backup has timed out & no longer running.
     $arguments = Ithemes_Sync_Functions::merge_defaults($arguments, $this->default_arguments);
     if (!class_exists('backupbuddy_core')) {
         require_once pb_backupbuddy::plugin_path() . '/classes/core.php';
     }
     /***** BEGIN CALCULATING CURRENT BACKUP DETAILS *****/
     require_once pb_backupbuddy::plugin_path() . '/classes/fileoptions.php';
     $backup_options = new pb_backupbuddy_fileoptions(backupbuddy_core::getLogDirectory() . 'fileoptions/' . pb_backupbuddy::$options['last_backup_serial'] . '.txt', $read_only = true);
     if (true !== ($result = $backup_options->is_ok())) {
         $currentBackup = false;
     } else {
         $currentBackup = $backup_options->options;
     }
     $currentBackupStats['serial'] = $currentBackup['serial'];
     $currentBackupStats['isRunning'] = '0';
     if ('0' == $currentBackup['finish_time']) {
         // Last backup has not finished yet or timed out.
         if (time() - $currentBackup['updated_time'] > 60 * $timeoutMinutes) {
             // Most likely timed out.
         } else {
             // Still chugging along possibly.
             $currentBackupStats['isRunning'] = '1';
         }
     }
     $currentBackupStats['processStarted'] = $currentBackup['start_time'];
     $currentBackupStats['processFinished'] = $currentBackup['finish_time'];
     $currentBackupStats['processStepTitle'] = '';
     $currentBackupStats['processStepFunction'] = '';
     $currentBackupStats['processStepElapsed'] = 0;
     foreach ((array) $currentBackup['steps'] as $step) {
         if ('0' == $step['finish_time']) {
             $currentBackupStats['processStepTitle'] = backupbuddy_core::prettyFunctionTitle($step['function']);
             $currentBackupStats['processStepFunction'] = $step['function'];
             $currentBackupStats['processStepElapsed'] = time() - $step['start_time'];
             break;
         }
     }
     $currentBackupStats['backupType'] = $currentBackup['type'];
     $currentBackupStats['profileTitle'] = htmlentities($currentBackup['profile']['title']);
     $currentBackupStats['scheduleTitle'] = $currentBackup['schedule_title'];
     if (@file_exists($currentBackup['archive_file'])) {
         $currentBackupStats['archiveFile'] = basename($currentBackup['archive_file']);
     } else {
         $currentBackupStats['archiveFile'] = '';
     }
     $currentBackupStats['archiveURL'] = '';
     if (isset($currentBackup['archive_url'])) {
         $currentBackupStats['archiveURL'] = $currentBackup['archive_url'];
     }
     $currentBackupStats['archiveSize'] = 0;
     if ($currentBackup['archive_size'] == 0) {
         if (file_exists($currentBackup['temporary_zip_directory'])) {
             // Temp zip file.
             $directory = opendir($currentBackup['temporary_zip_directory']);
             while ($file = readdir($directory)) {
                 if ($file != '.' && $file != '..' && $file != 'exclusions.txt' && !preg_match('/.*\\.txt/', $file) && !preg_match('/pclzip.*\\.gz/', $file)) {
                     $stats = stat($currentBackup['temporary_zip_directory'] . $file);
                     $currentBackupStats['archiveSize'] = $stats['size'];
                 }
             }
             closedir($directory);
             unset($directory);
         }
     }
     $integrityIsOK = '-1';
     if (isset($currentBackup['integrity']) && isset($currentBackup['integrity']['is_ok'])) {
         $integrityIsOK = $currentBackup['integrity']['is_ok'];
     }
     $currentBackupStats['integrityStatus'] = $integrityIsOK;
     // true, false, -1 (unknown)
     $destinations = array();
     foreach ((array) $currentBackup['steps'] as $step) {
         if ('send_remote_destination' == $step['function']) {
             $destinations[] = array('id' => $step['args'][0], 'title' => pb_backupbuddy::$options['remote_destinations'][$step['args'][0]]['title'], 'type' => pb_backupbuddy::$options['remote_destinations'][$step['args'][0]]['type']);
         }
     }
     $currentBackupStats['destinations'] = $destinations;
     // Index is destination ID. Empty array if none.
     /***** END CALCULATING CURRENT BACKUP DETAILS *****/
     return array('version' => '4', 'status' => 'ok', 'message' => 'Latest backup process details retrieved successfully.', 'latestBackupProcess' => $currentBackupStats, 'localTime' => time());
 }