Exemple #1
0
 public static function test($settings)
 {
     $settings = self::_init($settings);
     $sendOK = false;
     $deleteOK = false;
     $send_id = 'TEST-' . pb_backupbuddy::random_string(12);
     // Try sending a file.
     if ('1' == $settings['stash_mode']) {
         // Stash mode.
         $settings['type'] = 'stash2';
     }
     $send_response = pb_backupbuddy_destinations::send($settings, dirname(dirname(__FILE__)) . '/remote-send-test.php', $send_id);
     // 3rd param true forces clearing of any current uploads.
     if (true === $send_response) {
         $send_response = __('Success.', 'it-l10n-backupbuddy');
         $sendOK = true;
     } else {
         global $pb_backupbuddy_destination_errors;
         $send_response = 'Error sending test file to S3 (v2). Details: `' . implode(', ', $pb_backupbuddy_destination_errors) . '`.';
     }
     pb_backupbuddy::add_status_serial('remote_send-' . $send_id);
     // Delete sent file if it was sent.
     $delete_response = 'n/a';
     if (true === $sendOK) {
         pb_backupbuddy::status('details', 'Preparing to delete sent test file.');
         if ('1' == $settings['stash_mode']) {
             // Stash mode.
             if (true === ($delete_response = pb_backupbuddy_destination_stash2::deleteFile($settings, 'remote-send-test.php'))) {
                 // success
                 $delete_response = __('Success.', 'it-l10n-backupbuddy');
                 $deleteOK = true;
             } else {
                 // error
                 $error = 'Unable to delete Stash test file `remote-send-test.php`. Details: `' . $delete_response . '`.';
                 $delete_response = $error;
                 $deleteOK = false;
             }
         } else {
             // S3 mode.
             if (true === ($delete_response = self::deleteFile($settings, 'remote-send-test.php'))) {
                 $delete_response = __('Success.', 'it-l10n-backupbuddy');
                 $deleteOK = true;
             } else {
                 $error = 'Unable to delete test file `remote-send-test.php`. Details: `' . $delete_response . '`.';
                 pb_backupbuddy::status('details', $error);
                 $delete_response = $error;
                 $deleteOK = false;
             }
         }
     } else {
         // end if $sendOK.
         pb_backupbuddy::status('details', 'Skipping test delete due to failed send.');
     }
     // Load destination fileoptions.
     pb_backupbuddy::status('details', 'About to load fileoptions data.');
     require_once pb_backupbuddy::plugin_path() . '/classes/fileoptions.php';
     pb_backupbuddy::status('details', 'Fileoptions instance #7.');
     $fileoptions_obj = new pb_backupbuddy_fileoptions(backupbuddy_core::getLogDirectory() . 'fileoptions/send-' . $send_id . '.txt', $read_only = false, $ignore_lock = false, $create_file = false);
     if (true !== ($result = $fileoptions_obj->is_ok())) {
         return self::_error(__('Fatal Error #9034.84838. Unable to access fileoptions data.', 'it-l10n-backupbuddy') . ' Error: ' . $result);
     }
     pb_backupbuddy::status('details', 'Fileoptions data loaded.');
     $fileoptions =& $fileoptions_obj->options;
     if (true !== $sendOK || true !== $deleteOK) {
         $fileoptions['status'] = 'failure';
         $fileoptions_obj->save();
         unset($fileoptions_obj);
         return 'Send details: `' . $send_response . '`. Delete details: `' . $delete_response . '`.';
     } else {
         $fileoptions['status'] = 'success';
         $fileoptions['finish_time'] = time();
     }
     $fileoptions_obj->save();
     unset($fileoptions_obj);
     pb_backupbuddy::status('details', 'Finished test function.');
     return true;
 }
         echo ' ' . __('To jump right in using the defaults just hit "Add Destination" below.', 'it-l10n-backupbuddy');
     } else {
         echo '<div style="text-align: center;">' . $account_details . '</div>';
     }
     if ($mode == 'add') {
         // Check to see if user already has a Stash with this username set up for this site. No need for multiple same account Stashes.
         foreach ((array) pb_backupbuddy::$options['remote_destinations'] as $destination) {
             if (isset($destination['itxapi_username']) && strtolower($destination['itxapi_username']) == strtolower($itxapi_username)) {
                 echo '<br><br>';
                 pb_backupbuddy::alert('Note: You already have a Stash destination set up under the provided iThemes account username.  It is unnecessary to create multiple Stash destinations that go to the same user account as they are effectively the same destination and a duplicate.');
                 break;
             }
         }
     }
     echo '<br><br>';
     echo pb_backupbuddy_destination_stash2::get_quota_bar($account_info);
     echo '<!-- STASH DETAILS: ' . print_r($account_info, true) . ' -->';
 }
 // end if NOT in save mode.
 // Form settings.
 $settings_form->add_setting(array('type' => 'text', 'name' => 'title', 'title' => __('Destination name', 'it-l10n-backupbuddy'), 'tip' => __('Name of the new destination to create. This is for your convenience only.', 'it-l10n-backupbuddy'), 'rules' => 'required|string[1-45]', 'default' => $default_name));
 $settings_form->add_setting(array('type' => 'text', 'name' => 'full_archive_limit', 'title' => __('Full backup limit', 'it-l10n-backupbuddy'), 'tip' => __('[Example: 5] - Enter 0 for no limit. This is the maximum number of Full (complete) backup archives to be stored in this specific destination. If this limit is met the oldest backup of this type will be deleted.', 'it-l10n-backupbuddy'), 'rules' => 'int[0-9999999]', 'css' => 'width: 50px;', 'after' => ' backups. &nbsp;<span class="description">0 or blank for no limit.</span>'));
 $settings_form->add_setting(array('type' => 'text', 'name' => 'db_archive_limit', 'title' => __('Database only limit', 'it-l10n-backupbuddy'), 'tip' => __('[Example: 5] - Enter 0 for no limit. This is the maximum number of Database Only backup archives to be stored in this specific destination. If this limit is met the oldest backup of this type will be deleted.', 'it-l10n-backupbuddy'), 'rules' => 'int[0-9999999]', 'css' => 'width: 50px;', 'after' => ' backups. &nbsp;<span class="description">0 or blank for no limit.</span>'));
 $settings_form->add_setting(array('type' => 'text', 'name' => 'files_archive_limit', 'title' => __('Files only limit', 'it-l10n-backupbuddy'), 'tip' => __('[Example: 5] - Enter 0 for no limit. This is the maximum number of Files Only backup archives to be stored in this specific destination. If this limit is met the oldest backup of this type will be deleted.', 'it-l10n-backupbuddy'), 'rules' => 'int[0-9999999]', 'css' => 'width: 50px;', 'after' => ' backups. &nbsp;<span class="description">0 or blank for no limit.</span>'));
 $settings_form->add_setting(array('type' => 'title', 'name' => 'advanced_begin', 'title' => '<span class="dashicons dashicons-arrow-right"></span> ' . __('Advanced Options', 'it-l10n-backupbuddy'), 'row_class' => 'advanced-toggle-title'));
 $settings_form->add_setting(array('type' => 'text', 'name' => 'max_burst', 'title' => __('Send per burst', 'it-l10n-backupbuddy'), 'tip' => __('[Example: 10] - This is the amount of data that will be sent per burst within a single PHP page load/chunk. Bursts happen within a single page load. Chunks occur when broken up between page loads/PHP instances. Reduce if hitting PHP memory limits. Chunking time limits will only be checked between bursts. Lower burst size if timeouts occur before chunking checks trigger.', 'it-l10n-backupbuddy'), 'rules' => 'required|int[0-9999999]', 'css' => 'width: 50px;', 'after' => ' MB', 'row_class' => 'advanced-toggle'));
 $settings_form->add_setting(array('type' => 'text', 'name' => 'max_time', 'title' => __('Max time per chunk', 'it-l10n-backupbuddy'), 'tip' => __('[Example: 30] - Enter 0 for no limit (aka no chunking; bursts may still occur based on burst size setting). This is the maximum number of seconds per page load that bursts will occur. If this time is exceeded when a burst finishes then the next burst will be chunked and ran on a new page load. Multiple bursts may be sent within each chunk.', 'it-l10n-backupbuddy'), 'rules' => '', 'css' => 'width: 50px;', 'after' => ' secs. <span class="description">' . __('Blank for detected default:', 'it-l10n-backupbuddy') . ' ' . backupbuddy_core::detectMaxExecutionTime() . ' sec</span>', 'row_class' => 'advanced-toggle'));
 $settings_form->add_setting(array('type' => 'checkbox', 'name' => 'ssl', 'options' => array('unchecked' => '0', 'checked' => '1'), 'title' => __('Encrypt connection', 'it-l10n-backupbuddy') . '*', 'tip' => __('[Default: enabled] - When enabled, all transfers will be encrypted with SSL encryption. Disabling this may aid in connection troubles but results in lessened security. Note: Once your files arrive on our server they are encrypted using AES256 encryption. They are automatically decrypted upon download as needed.', 'it-l10n-backupbuddy'), 'css' => '', 'after' => '<span class="description"> ' . __('Enable connecting over SSL.', 'it-l10n-backupbuddy') . '<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;* Files are always encrypted with AES256 upon arrival.</span>', 'rules' => '', 'row_class' => 'advanced-toggle'));
 $settings_form->add_setting(array('type' => 'checkbox', 'name' => 'use_packaged_cert', 'options' => array('unchecked' => '0', 'checked' => '1'), 'title' => __('Use included CA bundle', 'it-l10n-backupbuddy'), 'tip' => __('[Default: disabled] - When enabled, BackupBuddy will use its own bundled SSL certificate bundle for connecting to the server. Use this if SSL fails due to SSL certificate issues with your server.', 'it-l10n-backupbuddy'), 'css' => '', 'after' => '<span class="description"> ' . __('Use included certificate bundle.', 'it-l10n-backupbuddy') . '</span>', 'rules' => '', 'row_class' => 'advanced-toggle'));
 if ($mode !== 'edit') {
     $settings_form->add_setting(array('type' => 'checkbox', 'name' => 'manage_all_files', 'options' => array('unchecked' => '0', 'checked' => '1'), 'title' => __('Manage all files', 'it-l10n-backupbuddy'), 'tip' => __('[Default: enabled] - When enabled, you have access to manage and view all files stored in your Stash account. You will be prompted for your password to access backups for sites other than this one.  If disabled the option is entirely removed for added security. For example, you may wish to disable this feature if a client has access and you want to keep them away from your files. This option can NOT be changed without deleting and re-creating the Stash destination for added security.', 'it-l10n-backupbuddy'), 'css' => '', 'rules' => '', 'row_class' => 'advanced-toggle'));
Exemple #3
0
    }
    $last_modified = $file['uploaded_timestamp'];
    $size = (double) $file['size'];
    $backup_type = backupbuddy_core::getBackupTypeFromFile($file['filename']);
    // Generate array of table rows.
    while (isset($backup_list_temp[$last_modified])) {
        // Avoid collisions.
        $last_modified += 0.1;
    }
    $backup_list_temp[$last_modified] = array(array(base64_encode($file['url']), $file['filename']), pb_backupbuddy::$format->date(pb_backupbuddy::$format->localize_time($last_modified)) . '<br /><span class="description">(' . pb_backupbuddy::$format->time_ago($last_modified) . ' ago)</span>', pb_backupbuddy::$format->file_size($size), backupbuddy_core::pretty_backup_type($backup_type));
}
krsort($backup_list_temp);
$backup_list = array();
foreach ($backup_list_temp as $backup_item) {
    $backup_list[$backup_item[0][0]] = $backup_item;
}
unset($backup_list_temp);
$urlPrefix = pb_backupbuddy::ajax_url('remoteClient') . '&destination_id=' . htmlentities(pb_backupbuddy::_GET('destination_id'));
$quota = pb_backupbuddy_destination_stash2::get_quota($settings);
echo pb_backupbuddy_destination_stash2::get_quota_bar($quota);
// Render table listing files.
if (count($backup_list) == 0) {
    echo '<b>';
    _e('You have not completed sending any backups to this S3 destination for this site yet.', 'it-l10n-backupbuddy');
    echo '</b>';
} else {
    pb_backupbuddy::$ui->list_table($backup_list, array('action' => pb_backupbuddy::ajax_url('remoteClient') . '&function=remoteClient&destination_id=' . htmlentities(pb_backupbuddy::_GET('destination_id')) . '&remote_path=' . htmlentities(pb_backupbuddy::_GET('remote_path')), 'columns' => array('Backup File', 'Uploaded <img src="' . pb_backupbuddy::plugin_url() . '/images/sort_down.png" style="vertical-align: 0px;" title="Sorted most recent first">', 'File Size', 'Type'), 'hover_actions' => array($urlPrefix . '&cpy_file=' => 'Copy to Local', $urlPrefix . '&downloadlink_file=' => 'Get download link'), 'hover_action_column_key' => '0', 'bulk_actions' => array('delete_backup' => 'Delete'), 'css' => 'width: 100%;'));
}
// Display troubleshooting subscriber key.
echo '<br style="clear: both;">';
return;
                 // Good so far.
                 if (!isset($response['error'])) {
                     // No error.
                     if (isset($response['token'])) {
                         $itxapi_token = $response['token'];
                     }
                 }
             }
             if (isset($itxapi_token)) {
                 // Build new Stash2 destination over old v1 destination based on old settings.
                 $v2_dest = array('type' => 'stash2', 'title' => $v1_dest['title'] . ' (v2)', 'itxapi_username' => $itxapi_username, 'itxapi_token' => $itxapi_token, 'ssl' => $v1_dest['ssl'], 'db_archive_limit' => $v1_dest['db_archive_limit'], 'full_archive_limit' => $v1_dest['full_archive_limit'], 'files_archive_limit' => $v1_dest['files_archive_limit'], 'manage_all_files' => $v1_dest['manage_all_files'], 'use_packaged_cert' => $v1_dest['use_packaged_cert'], 'disable_file_management' => $v1_dest['disable_file_management'], 'disabled' => $v1_dest['disabled']);
                 $v2_dest = array_merge(pb_backupbuddy_destination_stash2::$default_settings, $v2_dest);
                 // Merge over defaults.
             }
             $v2_dest['disabled'] = '0';
             if (true === pb_backupbuddy_destination_stash2::test($v2_dest)) {
                 $v2_dest['disabled'] = $v1_dest['disabled'];
                 pb_backupbuddy::$options['remote_destinations'][$destination_id] = $v2_dest;
                 // New stash2 replacing stash1 settings.
                 $v1_dest['disabled'] = '1';
                 // Disable old Stash v1 destination at new index.
                 $v1_dest['title'] = $v1_dest['title'] . ' (v1)';
                 pb_backupbuddy::$options['remote_destinations'][$nextDestKey] = $v1_dest;
                 // New location for old stash1 settings.
                 $nextDestKey++;
             }
         }
         // end foreach destination.
     }
     // Sufficient PHPversion.
 }
Exemple #5
0
 public static function send($settings = array(), $file, $send_id = '', $delete_after = false)
 {
     global $pb_backupbuddy_destination_errors;
     if ('1' == $settings['disabled']) {
         $pb_backupbuddy_destination_errors[] = __('Error #48933: This destination is currently disabled. Enable it under this destination\'s Advanced Settings.', 'it-l10n-backupbuddy');
         return false;
     }
     $settings = self::_init($settings);
     // Handles formatting & sanitizing settings.
     $chunkSizeBytes = $settings['max_burst'] * 1024 * 1024;
     // Send X mb at a time to limit memory usage.
     self::$_timeStart = microtime(true);
     // Initiate multipart upload.
     if ('' == $settings['_multipart_id']) {
         // New transfer. Note: All transfers are handled as presumed multiparts for ease.
         // Handle chunking of file into a multipart upload (if applicable).
         $file_size = filesize($file);
         pb_backupbuddy::status('details', 'File size of `' . pb_backupbuddy::$format->file_size($file_size) . '`.');
         // About to chunk so cleanup any previous hanging multipart transfers.
         self::multipart_cleanup($settings);
         // Initiate multipart upload with S3.
         pb_backupbuddy::status('details', 'Initiating multipart transfer.');
         try {
             $response = self::$_client->createMultipartUpload(array('Bucket' => $settings['bucket'], 'Key' => $settings['directory'] . basename($file), 'StorageClass' => $settings['storage'], 'ServerSideEncryption' => 'AES256'));
         } catch (Exception $e) {
             return self::_error('Error #389383: Unable to initiate multipart upload. Details: `' . $e->getMessage() . '`.');
         }
         // Made it here so SUCCESS initiating multipart!
         $upload_id = (string) $response['UploadId'];
         pb_backupbuddy::status('details', 'Initiated multipart upload with ID `' . $upload_id . '`.');
         $backup_type = backupbuddy_core::getBackupTypeFromFile($file);
         // Calculate multipart settings.
         $multipart_destination_settings = $settings;
         $multipart_destination_settings['_multipart_id'] = $upload_id;
         $multipart_destination_settings['_multipart_partnumber'] = 0;
         $multipart_destination_settings['_multipart_file'] = $file;
         $multipart_destination_settings['_multipart_remotefile'] = $settings['directory'] . basename($file);
         $multipart_destination_settings['_multipart_counts'] = self::_get_multipart_counts($file_size, $settings['max_burst'] * 1024 * 1024);
         // Size of chunks expected to be in bytes.
         $multipart_destination_settings['_multipart_backup_type'] = $backup_type;
         $multipart_destination_settings['_multipart_backup_size'] = $file_size;
         $multipart_destination_settings['_multipart_etag_parts'] = array();
         //pb_backupbuddy::status( 'details', 'Multipart settings to pass:'******'_multipart_status'] = 'Starting send of ' . count( $multipart_destination_settings['_multipart_counts'] ) . ' parts.';
         pb_backupbuddy::status('details', 'Multipart initiated; passing over to send first chunk this run.');
         $settings = $multipart_destination_settings;
         // Copy over settings.
         unset($multipart_destination_settings);
     }
     // end initiating multipart.
     // Send parts.
     $backup_type = str_replace('/', '', $settings['_multipart_backup_type']);
     // For use later by file limiting.
     $backup_size = $settings['_multipart_backup_size'];
     $maxTime = $settings['max_time'];
     if ('' == $maxTime || !is_numeric($maxTime)) {
         pb_backupbuddy::status('details', 'Max time not set in settings so detecting server max PHP runtime.');
         $maxTime = backupbuddy_core::detectMaxExecutionTime();
     }
     pb_backupbuddy::status('details', 'Using max runtime: `' . $maxTime . '`.');
     // Open file for streaming.
     $f = @fopen($settings['_multipart_file'], 'r');
     if (false === $f) {
         return self::_error('Error #437734. Unable to open file `' . $settings['_multipart_file'] . '` to send. Did it get deleted?');
     }
     $fileDone = false;
     while (!$fileDone && !feof($f)) {
         $sendStart = microtime(true);
         // Made it here so success sending part. Increment for next part to send.
         $settings['_multipart_partnumber']++;
         if (!isset($settings['_multipart_counts'][$settings['_multipart_partnumber'] - 1]['seekTo'])) {
             pb_backupbuddy::status('error', 'Error #8239933: Missing multipart partnumber to seek to. Settings array: `' . print_r($settings, true) . '`.');
         }
         if (-1 == fseek($f, (int) $settings['_multipart_counts'][$settings['_multipart_partnumber'] - 1]['seekTo'])) {
             return self::_error('Error #833838: Unable to fseek file.');
         }
         pb_backupbuddy::status('details', 'Beginning upload of part `' . $settings['_multipart_partnumber'] . '` of `' . count($settings['_multipart_counts']) . '` parts of file `' . $settings['_multipart_file'] . '` to remote location `' . $settings['_multipart_remotefile'] . '` with multipart ID `' . $settings['_multipart_id'] . '`.');
         $contentLength = (int) $settings['_multipart_counts'][$settings['_multipart_partnumber'] - 1]['length'];
         try {
             $uploadArr = array('Bucket' => $settings['bucket'], 'Key' => $settings['_multipart_remotefile'], 'UploadId' => $settings['_multipart_id'], 'PartNumber' => $settings['_multipart_partnumber'], 'ContentLength' => $contentLength, 'Body' => fread($f, $contentLength));
             //pb_backupbuddy::status( 'details', 'Send array: `' . print_r( $uploadArr, true ) . '`.' );
             $response = self::$_client->uploadPart($uploadArr);
         } catch (Exception $e) {
             @fclose($f);
             return self::_error('Unable to upload file part for multipart upload of ID `' . $settings['_multipart_id'] . '`. Details: `' . $e->getMessage() . '`.');
         }
         self::$_chunksSentThisRound++;
         $settings['_multipart_etag_parts'][] = array('PartNumber' => $settings['_multipart_partnumber'], 'ETag' => $response['ETag']);
         if (pb_backupbuddy::$options['log_level'] == '3') {
             // Full logging enabled.
             pb_backupbuddy::status('details', 'Success sending chunk. Upload details due to log level: `' . print_r($response, true) . '`.');
         } else {
             pb_backupbuddy::status('details', 'Success sending chunk. Enable full logging for upload result details.');
         }
         $uploaded_size = $contentLength;
         $elapseTime = microtime(true) - $sendStart;
         if (0 == $elapseTime) {
             $elapseTime = 1;
         }
         $uploaded_speed = $uploaded_size / $elapseTime;
         pb_backupbuddy::status('details', 'Uploaded size this burst: `' . pb_backupbuddy::$format->file_size($uploaded_size) . '`, Start time: `' . $sendStart . '`. Finish time: `' . microtime(true) . '`. Elapsed: `' . (microtime(true) - $sendStart) . '`. Speed: `' . pb_backupbuddy::$format->file_size($uploaded_speed) . '`/sec.');
         // Load fileoptions to the send.
         if (isset($fileoptions_obj)) {
             pb_backupbuddy::status('details', 'fileoptions already loaded from prior pass.');
         } else {
             // load fileoptions
             pb_backupbuddy::status('details', 'About to load fileoptions data.');
             require_once pb_backupbuddy::plugin_path() . '/classes/fileoptions.php';
             pb_backupbuddy::status('details', 'Fileoptions instance #10.');
             $fileoptions_obj = new pb_backupbuddy_fileoptions(backupbuddy_core::getLogDirectory() . 'fileoptions/send-' . $send_id . '.txt', $read_only = false, $ignore_lock = false, $create_file = false);
             if (true !== ($result = $fileoptions_obj->is_ok())) {
                 return self::_error(__('Fatal Error #9034.2344848. Unable to access fileoptions data.', 'it-l10n-backupbuddy') . ' Error: ' . $result);
             }
             pb_backupbuddy::status('details', 'Fileoptions data loaded.');
             $fileoptions =& $fileoptions_obj->options;
         }
         //$update_status = 'Sent part ' . $settings['_multipart_partnumber'] . ' of ' . count( $settings['_multipart_counts'] ) . '.';
         if (!isset($settings['_multipart_counts'][$settings['_multipart_partnumber']])) {
             // No more parts exist for this file. Tell S3 the multipart upload is complete and move on.
             pb_backupbuddy::status('details', 'S3 getting parts with etags to notify S3 of completed multipart send.');
             $update_status = 'Sent part ' . $settings['_multipart_partnumber'] . ' of ' . count($settings['_multipart_counts']) . ' parts.';
             pb_backupbuddy::status('details', 'Notifying server of multipart upload completion.');
             try {
                 $response = self::$_client->completeMultipartUpload(array('Bucket' => $settings['bucket'], 'UploadId' => $settings['_multipart_id'], 'Key' => $settings['_multipart_remotefile'], 'Parts' => $settings['_multipart_etag_parts']));
             } catch (Exception $e) {
                 return self::_error('Unable to notify server of completion of all parts for multipart upload `' . $settings['_multipart_id'] . '`. Details: `' . $e->getMessage() . '`.');
             }
             pb_backupbuddy::status('details', 'Server notified of multipart completion.');
             pb_backupbuddy::status('details', 'No more parts left for this multipart upload. Clearing multipart instance variables.');
             $settings['_multipart_partnumber'] = 0;
             $settings['_multipart_id'] = '';
             $settings['_multipart_file'] = '';
             $settings['_multipart_remotefile'] = '';
             // Multipart completed so safe to prevent housekeeping of incomplete multipart uploads.
             $settings['_multipart_transferspeeds'][] = $uploaded_speed;
             // Overall upload speed average.
             $uploaded_speed = array_sum($settings['_multipart_transferspeeds']) / count($settings['_multipart_counts']);
             pb_backupbuddy::status('details', 'Upload speed average of all chunks: `' . pb_backupbuddy::$format->file_size($uploaded_speed) . '`.');
             $settings['_multipart_counts'] = array();
             // Update stats.
             $fileoptions['_multipart_status'] = $update_status;
             $fileoptions['finish_time'] = microtime(true);
             $fileoptions['status'] = 'success';
             if (isset($uploaded_speed)) {
                 $fileoptions['write_speed'] = $uploaded_speed;
             }
             $fileoptions_obj->save();
             unset($fileoptions);
             $fileDone = true;
             @fclose($f);
         } else {
             // Parts remain. Schedule to continue if anything is left to upload for this multipart of any individual files.
             pb_backupbuddy::status('details', 'S3 multipart upload has more parts left.');
             $update_status = '<br>';
             $totalSent = 0;
             for ($i = 0; $i < $settings['_multipart_partnumber']; $i++) {
                 $totalSent += $settings['_multipart_counts'][$i]['length'];
             }
             $percentSent = ceil($totalSent / $settings['_multipart_backup_size'] * 100);
             $update_status .= '<div class="backupbuddy-progressbar" data-percent="' . $percentSent . '"><div class="backupbuddy-progressbar-label"></div></div>';
             if ('0' != $maxTime) {
                 // Not unlimited time so see if we can send more bursts this time or if we need to chunk.
                 // If we are within X second of reaching maximum PHP runtime then stop here so that it can be picked up in another PHP process...
                 $totalSizeSent = self::$_chunksSentThisRound * $chunkSizeBytes;
                 // Total bytes sent this PHP load.
                 $bytesPerSec = $totalSizeSent / (microtime(true) - $sendStart);
                 $timeRemaining = $maxTime - (microtime(true) - self::$_timeStart + self::TIME_WIGGLE_ROOM);
                 if ($timeRemaining < 0) {
                     $timeRemaining = 0;
                 }
                 $bytesWeCouldSendWithTimeLeft = $bytesPerSec * $timeRemaining;
                 //pb_backupbuddy::status( 'details', 'Sent this burst: `' . pb_backupbuddy::$format->file_size( $totalSizeSent ) .'` in `' . (microtime(true) - $sendStart ) . '` secs. Speed: `' . pb_backupbuddy::$format->file_size( $bytesPerSec ) . '`/sec. Time Remaining (w/ wiggle): `' . $timeRemaining . '`. Size that could potentially be sent with remaining time: `' . pb_backupbuddy::$format->file_size( $bytesWeCouldSendWithTimeLeft ) . '` with chunk size of `' . pb_backupbuddy::$format->file_size( $chunkSizeBytes ) . '`.' );
                 if ($bytesWeCouldSendWithTimeLeft < $chunkSizeBytes) {
                     // We can send more than a whole chunk (including wiggle room) so send another bit.
                     pb_backupbuddy::status('message', 'Not enough time left (~`' . $timeRemaining . '`) with max time of `' . $maxTime . '` sec to send another chunk at `' . pb_backupbuddy::$format->file_size($bytesPerSec) . '` / sec. Ran for ' . round(microtime(true) - self::$_timeStart, 3) . ' sec. Proceeding to use chunking.');
                     @fclose($fs);
                     $cronTime = time();
                     $cronArgs = array($settings, $file, $send_id, $delete_after);
                     $cronHashID = md5($cronTime . serialize($cronArgs));
                     $cronArgs[] = $cronHashID;
                     $schedule_result = backupbuddy_core::schedule_single_event($cronTime, pb_backupbuddy::cron_tag('destination_send'), $cronArgs);
                     if (true === $schedule_result) {
                         pb_backupbuddy::status('details', 'Next S3 chunk step cron event scheduled.');
                     } else {
                         pb_backupbuddy::status('error', 'Next S3 chunk step cron even FAILED to be scheduled.');
                     }
                     spawn_cron(time() + 150);
                     // Adds > 60 seconds to get around once per minute cron running limit.
                     update_option('_transient_doing_cron', 0);
                     // Prevent cron-blocking for next item.
                     @fclose($f);
                     unset($fileoptions);
                     return array($settings['_multipart_id'], 'Sent part ' . $settings['_multipart_partnumber'] . ' of ' . count($settings['_multipart_counts']) . ' parts.' . $update_status);
                 } else {
                     // End if.
                     pb_backupbuddy::status('details', 'Not approaching limits. Proceeding to next burst this run.');
                 }
             } else {
                 pb_backupbuddy::status('details', 'Max time of zero (0) so assuming unlimited time.');
             }
             $fileoptions['_multipart_status'] = 'Sent part ' . $settings['_multipart_partnumber'] . ' of ' . count($settings['_multipart_counts']) . ' parts.' . $update_status;
             $fileoptions_obj->save();
             //unset( $fileoptions );
         }
         // end no more parts remain.
     }
     // End while not feof.
     /***** BEGIN FILE ARCHIVE LIMITS *****/
     if ('1' == $settings['stash_mode']) {
         // This is being wrapped by the Stash destination. Stash uses a different method of handling archive limiting due to using Stash API.
         pb_backupbuddy_destination_stash2::archiveLimit($settings, $backup_type);
     } else {
         // Normal. This is just a s32 destination.
         self::archiveLimit($settings, $backup_type);
     }
     /***** END FILE ARCHIVE LIMITS *****/
     if (isset($fileoptions_obj)) {
         unset($fileoptions_obj);
     }
     // Success if we made it this far.
     return true;
 }
 // See if this user already exists.
 foreach (pb_backupbuddy::$options['remote_destinations'] as $destination_index => $destination) {
     // Loop through ending with the last created destination of this type.
     if ('stash2' == $destination['type']) {
         if ($itxapi_username == $destination['itxapi_username']) {
             // Existing destination match.
             $destination_id = $destination_index;
         }
     }
 }
 if (!isset($destination_id)) {
     // Did not already find the same Stash destination.
     $password_hash = iThemes_Credentials::get_password_hash($itxapi_username, pb_backupbuddy::_POST('stash2_password'));
     $access_token = ITXAPI_Helper2::get_access_token($itxapi_username, $password_hash, site_url(), $wp_version);
     $settings = array('itxapi_username' => $itxapi_username, 'itxapi_password' => $access_token);
     $response = pb_backupbuddy_destination_stash2::stashAPI($settings, 'connect');
     if (!is_array($response)) {
         // Error message.
         $errors[] = 'Error #32898973: Unexpected server response. Check your Stash login and try again. Detailed response: `' . print_r($response, true) . '`.';
     } else {
         if (isset($response['error'])) {
             $errors[] = $response['error']['message'];
         } else {
             if (isset($response['token'])) {
                 $itxapi_token = $response['token'];
             } else {
                 $errors[] = 'Error #32977932: Unexpected server response. Token missing. Check your Stash login and try again. Detailed response: `' . print_r($response, true) . '`.';
             }
         }
     }
     // If we have the token then create the Stash2 destination.
    }
    $last_modified = $file['uploaded_timestamp'];
    $size = (double) $file['size'];
    $backup_type = backupbuddy_core::getBackupTypeFromFile($file['filename']);
    // Generate array of table rows.
    while (isset($backup_list_temp[$last_modified])) {
        // Avoid collisions.
        $last_modified += 0.1;
    }
    $backup_list_temp[$last_modified] = array(array(base64_encode($file['url']), '<span title="' . $file['filename'] . '">' . basename($file['filename']) . '</span>'), pb_backupbuddy::$format->date(pb_backupbuddy::$format->localize_time($last_modified)) . '<br /><span class="description">(' . pb_backupbuddy::$format->time_ago($last_modified) . ' ago)</span>', pb_backupbuddy::$format->file_size($size), backupbuddy_core::pretty_backup_type($backup_type));
}
krsort($backup_list_temp);
$backup_list = array();
foreach ($backup_list_temp as $backup_item) {
    $backup_list[$backup_item[0][0]] = $backup_item;
}
unset($backup_list_temp);
$urlPrefix = pb_backupbuddy::ajax_url('remoteClient') . '&destination_id=' . htmlentities(pb_backupbuddy::_GET('destination_id'));
$quota = pb_backupbuddy_destination_stash2::get_quota($settings);
echo pb_backupbuddy_destination_stash2::get_quota_bar($quota, $settings, true);
// Render table listing files.
if (count($backup_list) == 0) {
    echo '<center><br><b>';
    _e('You have not completed sending any backups to this destination for this site yet.', 'it-l10n-backupbuddy');
    echo '</b></center>';
} else {
    pb_backupbuddy::$ui->list_table($backup_list, array('action' => pb_backupbuddy::ajax_url('remoteClient') . '&function=remoteClient&destination_id=' . htmlentities(pb_backupbuddy::_GET('destination_id')) . '&remote_path=' . htmlentities(pb_backupbuddy::_GET('remote_path')), 'columns' => array('Backup File', 'Uploaded <img src="' . pb_backupbuddy::plugin_url() . '/images/sort_down.png" style="vertical-align: 0px;" title="Sorted most recent first">', 'File Size', 'Type'), 'hover_actions' => array($urlPrefix . '&cpy_file=' => 'Copy to Local', $urlPrefix . '&downloadlink_file=' => 'Get download link'), 'hover_action_column_key' => '0', 'bulk_actions' => array('delete_backup' => 'Delete'), 'css' => 'width: 100%;'));
}
// Display troubleshooting subscriber key.
echo '<br style="clear: both;">';
return;
 public static function _send_dbqueue()
 {
     require_once pb_backupbuddy::plugin_path() . '/destinations/stash2/init.php';
     $response = pb_backupbuddy_destination_stash2::stashAPI(pb_backupbuddy::$options['remote_destinations'][backupbuddy_live::getLiveID()], 'live-put', array('files' => self::$_dbqueue_rendered));
     //error_log( 'Live db send response:' );
     //error_log( print_r( $response, true ) );
     $errors = array();
     if (!is_array($response)) {
         // Error message.
         $errors[] = 'Error #3279237: Unexpected server response. Check your BackupBuddy Stash Live login and try again. Detailed response: `' . print_r($response, true) . '`.';
     } else {
         // Errors.
         if (isset($response['error'])) {
             $errors[] = $response['error']['message'];
         } else {
             // No error?
             if (!isset($response['success']) || '1' != $response['success']) {
                 $errors[] = 'Error #9327324: Something went wrong. Success was not reported. Detailed response: `' . print_r($response, true) . '`.';
             }
         }
     }
     self::$_queue_sent = true;
     // Prevent potentially sending again if shutdown hook double-fires.
     if (count($errors) > 0) {
         pb_backupbuddy::status('error', 'Error sending live continuous data. Error(s): `' . implode(', ', $errors) . '`.');
         //backupbuddy_core::addNotification( 'live_continuous_error', 'BackupBuddy Stash Live Errors', implode( ', ', $errors ), $errors );
     } else {
         // Update last activity time.
         backupbuddy_live::update_db_live_activity_time();
         if (pb_backupbuddy::$options['log_level'] == '3') {
             // Full logging enabled.
             pb_backupbuddy::status('details', 'Success sending live continuous data to server.');
         }
     }
     return true;
 }
 private static function _request_kick_cron()
 {
     if (false !== self::_load_state()) {
         if (time() - self::$_state['stats']['last_kick_request'] < self::KICK_REQUEST_MINIMUM_PERIOD) {
             // Too soon.
             return false;
         }
         // Update last kick request time & save.
         self::$_state['stats']['last_kick_request'] = time();
         self::$_stateObj->save();
         // Request the kicks.
         require_once pb_backupbuddy::plugin_path() . '/destinations/stash2/init.php';
         pb_backupbuddy_destination_stash2::cron_kick_api(pb_backupbuddy::$options['remote_destinations'][backupbuddy_live::getLiveID()]);
         return true;
     }
     return false;
 }