function Process_Pull()
 {
     // Load status
     $this->Load_Status();
     // Clear any data we have in any WordPress buffers - should not get much due to POST but just in case
     $this->_Clear_Output_Buffers();
     // Avoid timeouts and do not ignore a client abort
     @set_time_limit(0);
     @ignore_user_abort(false);
     // Turn off HTML errors to try and ensure the data stream doesn't get tainted by any
     ini_set('display_errors', 0);
     ini_set('html_errors', 0);
     // Check we have a backup running
     if ($this->status['status'] != WPONLINEBACKUP_STATUS_RUNNING && $this->status['status'] != WPONLINEBACKUP_STATUS_TICKING && $this->status['status'] != WPONLINEBACKUP_STATUS_CHECKING) {
         return $this->_Pull_Failure('OBFWRF' . $this->status['status'] . ':' . (isset($this->status['progress']['comp']) ? $this->status['progress']['comp'] : '?'));
     }
     // If we're not an online backup, we shouldn't be retrieving
     if ($this->status['progress']['config']['target'] != 'online') {
         return $this->_Pull_Failure('OBFWRI2');
     }
     // Grab the variables
     $nonce = isset($_GET['wponlinebackup_fetch']) ? strval($_GET['wponlinebackup_fetch']) : strval($_POST['wponlinebackup_fetch']);
     $which = isset($_GET['which']) ? strval($_GET['which']) : (isset($_POST['which']) ? strval($_POST['which']) : '');
     $start = isset($_GET['start']) ? intval($_GET['start']) : (isset($_POST['start']) ? intval($_POST['start']) : 0);
     // Check the nonce
     if ($this->status['progress']['nonce'] == '') {
         return $this->_Pull_Failure('OBFWRI3');
     } else {
         if ($nonce != $this->status['progress']['nonce']) {
             return $this->_Pull_Failure('OBFWRI4');
         }
     }
     // Make sure which is acceptable
     $which = $which == 'data' ? 'data' : 'indx';
     // Check we're not starting the transfer past the end of the file
     if ($start > $this->status['progress']['file_set']['size'][$which]) {
         return $this->_Pull_Failure('OBFWRE1');
     }
     // Open the requested file - open in binary mode! We don't want any conversions happening
     // If this fails - we treat as a temporary condition since maybe the file isn't quite ready
     if (($f = @fopen($this->status['progress']['file_set']['file'][$which], 'rb')) === false) {
         return $this->_Pull_Delay('OBFWRE2 ' . OBFW_Exception());
     }
     if (@fseek($f, $this->status['progress']['file_set']['offset'][$which] + $start, SEEK_SET) != 0) {
         $ret = OBFW_Exception();
         @fclose($f);
         return $this->_Pull_Failure('OBFWRE3 ' . $ret);
     }
     // Send through a content-type header to stop any CDN or rogue plugin modifying our binary stream
     // We had an instance where 0x09 (tab) was getting replaced with 0x20 (space), corrupting the data stream
     header('Content-Type: application/octet-stream');
     // Send the length of the data we're about to pass through - this is OBFWRD (6) + Length of nonce + File Size - Start position
     header('Content-Length: ' . ($this->status['progress']['file_set']['size'][$which] - $start + strlen($this->status['progress']['nonce']) + 6));
     header('Content-Disposition: attachment; filename="backup.' . $which . '"');
     // Print the validation header
     echo 'OBFWRD' . $this->status['progress']['nonce'];
     // Passthrough
     @fpassthru($f);
     @fclose($f);
     return $this->_End_Request();
 }
示例#2
0
 function _Legacy_Decryption_Loop()
 {
     $finished = false;
     // Loop and decrypt
     while (!$finished) {
         // Grab a block size
         if (false === ($data_len = @fread($this->file, 4))) {
             return OBFW_Exception();
         }
         // Because we read exact amounts, until we TRY to read past the end of the file, feof will not return true yet
         // So although we may have read everything, feof returns false until we try the above read, and we'll get 0 bytes back and feof will then return true
         if (strlen($data_len) == 0 && @feof($this->file)) {
             break;
         }
         if (strlen($data_len) < 4) {
             return 'Partially read ' . strlen($data_len) . ' of 4 bytes from encrypted data file for a segment header.';
         }
         // Unpack data length
         list($len) = array_values(unpack('Nlen', $data_len));
         // This size should really be no more than ENCRYPTION_SEGMENT_SIZE in V1 plugin which was 1048576. We shall ensure it is not above 8MB in case people adjusted it
         if ($len > 1048576 * 8) {
             return 'A segment header appears to be corrupt.';
         }
         // Read the data
         if (false === ($data = @fread($this->file, $len))) {
             return OBFW_Exception();
         }
         if (strlen($data) < $len) {
             return 'Partially read ' . strlen($data) . ' of ' . $len . ' bytes from encrypted data file.';
         }
         // Decrypt the data
         $data = mdecrypt_generic($this->cipher, $data);
         // Check if we are done or not
         if ($this->job['header']['len'] !== false) {
             $this->job['done_bytes'] += $len;
             if ($this->job['done_bytes'] > $this->job['header']['len']) {
                 $data = substr($data, 0, strlen($data) - ($this->job['done_bytes'] - $this->job['header']['len']));
                 $finished = true;
             }
         }
         if (true !== ($ret = $this->stream->Write($data))) {
             return 'Write to stream failed. ' . $ret;
         }
         $this->job['done_bytes'] += $block;
         // Update the progress, but only if we have a len (it might be false)
         if ($this->job['header']['len'] !== false) {
             if ($this->job['done_bytes'] >= $this->job['header']['len']) {
                 $this->job['progress'] = 99;
             } else {
                 $this->job['progress'] = 10 + floor($this->job['done_bytes'] * 89 / $this->job['header']['len']);
                 if ($this->job['progress'] > 99) {
                     $this->job['progress'] = 99;
                 }
             }
         }
         $this->bootstrap->Tick();
     }
     return true;
 }
    function Open(&$disk, $cipher_spec, $key, $extra = 0, $rjct_intention = false)
    {
        // ASSERTION - The file is closed
        // First thing first, check we actually have encryption available... If not, server configuration has changed
        if (!$this->WPOnlineBackup->Get_Env('encryption_available') || !array_key_exists($cipher_spec, $this->WPOnlineBackup->Get_Env('encryption_types'))) {
            return __('The selected encryption type is no longer available on the server. The server configuration must have changed since encryption was configured. Please change the encryption details and run the backup again. If this was an online backup you may need to contact your host about this as your encryption details cannot be changed.', 'wponlinebackup');
        }
        // Store the cipher specification and key
        $this->cipher_spec = $cipher_spec;
        $this->key = $key;
        // Store the file handle
        $this->disk =& $disk;
        // Attempt to open the cipher module
        list($module, $module_str, $key_size) = $this->Get_Cipher($this->cipher_spec);
        if (($this->cipher = @mcrypt_module_open($module, '', MCRYPT_MODE_CBC, '')) === false) {
            return 'Failed to open encryption module. PHP: ' . OBFW_Exception();
        }
        // Get the IV size
        $iv_size = mcrypt_enc_get_iv_size($this->cipher);
        // Randomly generate an IV - the IV will be stored in the file
        $this->iv = '';
        mt_srand(time());
        for ($i = 0; $i < $iv_size; $i++) {
            $this->iv .= chr(rand(0, 255));
        }
        // Generate the encryption key and password authentication value - allow $extra parameter to use a different section of the key
        $dk = WPOnlineBackup_Functions::PBKDF2($this->key, $this->iv, 1148, $key_size * (2 + $extra) + 2);
        $this->key = substr($dk, $extra ? $key_size * (1 + $extra) + 2 : 0, $key_size);
        $pass_auth = substr($dk, $key_size * 2, 2);
        // Now initialise the cipher so we can start encrypting. Returns -2/-3 on errors, false on incorrect parameters
        if (($ret = @mcrypt_generic_init($this->cipher, $this->key, $this->iv)) === false || $ret < 0) {
            $e = 'Failed to initialise encryption. PHP: ' . OBFW_Exception();
            @mcrypt_module_close($this->cipher);
            return $e;
        }
        // Is the caller intending to write a rejection header through this writer?
        // If so, we'll have to write our own since the one we get given will be encrypted and mangled
        if ($rjct_intention) {
            // We don't need to store this, the disk itself will keep track of positioning, much like it will with our header
            // Decryption is not an issue since it will search for the OBFWEN descriptor in the first 1024 bytes
            $header = <<<HEADER
<?php
/*Rejection header*/
__halt_compiler();
}
HEADER;
            if (($ret = $this->disk->Write($header)) !== true) {
                @mcrypt_generic_deinit($this->cipher);
                @mcrypt_module_close($this->cipher);
                return $ret;
            }
        }
        // This must be called AFTER we write rejection header otherwise we'll fail to adjust the header during Close()
        $this->header_pos = $this->disk->Pos();
        // Write the file header
        // We used to encrypt the header but we don't any more so we can get the actual backup file size without needing the key
        // We'll set an unencrypted size of 0 and fill it in as we close
        $fields = array(array('a6', 'OBFWEN'), array('v', 3), array('v', 0), array('C', ord($pass_auth[0])), array('C', ord($pass_auth[1])), array('V', $iv_size), array('V', 0), array('V', 0), array('V', 0));
        $fields = WPOnlineBackup_Functions::Pack_Fields($fields) . $this->iv;
        if (($ret = $this->disk->Write($fields)) !== true) {
            @mcrypt_generic_deinit($this->cipher);
            @mcrypt_module_close($this->cipher);
            return $ret;
        }
        // Grab the real block size and adjust the configured block size to ensure it is an exact divisor
        $this->real_blocksize = mcrypt_enc_get_block_size($this->cipher);
        if (($rem = $this->WPOnlineBackup->Get_Setting('encryption_block_size') % $this->real_blocksize) != 0) {
            $this->blocksize = $this->WPOnlineBackup->Get_Setting('encryption_block_size') + ($this->real_blocksize - $rem);
        } else {
            $this->blocksize = $this->WPOnlineBackup->Get_Setting('encryption_block_size');
        }
        // Prepare the counters
        $this->data = '';
        $this->data_len = 0;
        if ($this->WPOnlineBackup->Get_Env('inc_hash_available')) {
            $this->hash_ctx = hash_init('crc32b');
        } else {
            $this->hash_ctx = false;
        }
        $this->hash_len = 0;
        $this->crc = false;
        $this->last_cipher = null;
        $this->totalsize = 0;
        $this->encsize = strlen($fields);
        $this->volatile = false;
        $this->volatile_len = 0;
        $this->buffer_disk = false;
        // Open() called so set status
        $this->status = 1;
        return true;
    }
示例#4
0
 function Send_Email()
 {
     global $wpdb;
     // Pre-calculate the backup size and store the text representation
     $text_size = WPOnlineBackup_Formatting::Fix_B($this->progress['file_set']['size'], true);
     // Change the progress message
     if ($this->job['progress'] == 0) {
         $this->progress['message'] = __('Sending email...', 'wponlinebackup');
         // Log the size of the backup to help with diagnosis using the event log
         $this->bootstrap->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, sprintf(__('The backup is %s in size. Emailing it to %s.', 'wponlinebackup'), $text_size, $this->progress['config']['email_to']));
         $this->job['progress'] = 1;
         // Force an update so we can properly catch retries
         $this->bootstrap->Tick(false, true);
     } else {
         if ($this->job['progress'] == 1) {
             // We're retrying, increase the count
             if ($this->job['retries']++ >= 2) {
                 $email_limit = $this->WPOnlineBackup->Get_Setting('max_email_size');
                 // We've tried twice and failed both times - memory usage is nearly always exactly the same on the 3rd and subsequent runs, so let's just die now and not postpone the inevitable
                 $this->bootstrap->Log_Event(WPONLINEBACKUP_EVENT_ERROR, sprintf(__('Two attempts to send the email timed out. It looked like PHP would have enough to memory to process backup files of up to %s, and your backup file is %s. There may be an issue with your WordPress installation\'s ability to send emails with attachments, or something is causing the emailing process to use more memory than it normally would. Try reducing the size of your backup by adding some exclusions, and check that any email-related plugins are functioning normally.', 'wponlinebackup'), WPOnlineBackup_Formatting::Fix_B($email_limit, true), $text_size) . PHP_EOL . 'Failed at: ' . __FILE__ . ':' . __LINE__);
                 return sprintf(__('There is a problem with your WordPress installation\'s ability to send emails, or the backup file is too large to send as an attachment (%s).', 'wponlinebackup'), $text_size);
             }
             // Force an update to save the new retry count
             $this->bootstrap->Tick(false, true);
         }
     }
     // Check we aren't too big to process
     // TODO: Once Impose_DataSize_Limit is implemented we'll be able to remove this check since we'll never actually get here
     if ($this->progress['file_set']['size'] > ($email_limit = $this->WPOnlineBackup->Get_Setting('max_email_size'))) {
         $this->bootstrap->Log_Event(WPONLINEBACKUP_EVENT_ERROR, sprintf(__('The amount of memory required to encode the backup into email format will use up more memory than PHP currently has available. Your backup is %s and PHP only has enough memory for a backup of approximately %s. Try reducing the size of your backup by adding some exclusions.', 'wponlinebackup'), $text_size, WPOnlineBackup_Formatting::Fix_B($email_limit, true)) . PHP_EOL . 'Failed at: ' . __FILE__ . ':' . __LINE__);
         return sprintf(__('The backup file is too large to send in an email (%s).', 'wponlinebackup'), $text_size);
     }
     // Open the backup file for reading into memory
     if (false === ($f = @fopen($this->progress['file_set']['file'], 'r'))) {
         return 'Failed to open the backup file for attaching to the email. PHP: ' . OBFW_Exception();
     }
     // Seek past the start
     if (0 !== @fseek($f, $this->progress['file_set']['offset'], SEEK_SET)) {
         return 'Failed to perpare the backup file for attaching to the email. PHP: ' . OBFW_Exception();
     }
     // Read all the data into an output buffer
     ob_start();
     if (false === @fpassthru($f)) {
         return 'Failed to read the backup file for attaching to the email. PHP: ' . OBFW_Exception();
     }
     // Grab the output buffer contents and immediately clear the output buffer to free memory
     $this->attachment_data = ob_get_contents();
     ob_end_clean();
     // Calculate the attachment filename
     $this->attachment_filename = 'WPOnlineBackup_Full';
     // Grab the extension from our file_set file, if we fail - give no extension... that would be a bug
     if (preg_match('#((?:\\.[a-z]+)+)\\.[A-Za-z0-9]+\\.php$#i', $this->progress['file_set']['file'], $matches)) {
         $this->attachment_filename .= $matches[1];
     }
     // Hook into the PHPMailer initialisation so we can borrow a reference to PHPMailer and add the attachment to the email with our own filename
     add_action('phpmailer_init', array(&$this, 'Action_PHPMailer_Init'));
     // Prepare the email body
     $body = sprintf(__('Online Backup for WordPress backup of %s successfully completed. The size of the backup is %s.', 'wponlinebackup'), site_url(), $text_size);
     // Require pluggable.php to define wp_mail
     require_once ABSPATH . 'wp-includes/pluggable.php';
     // Send the email
     if (@wp_mail($this->progress['config']['email_to'], sprintf(__('Backup of %s completed', 'wponlinebackup'), site_url()), $body, '') === false) {
         $error = OBFW_Exception();
         // Free memory in case it wasn't already
         unset($this->attachment_data);
         unset($this->attachment_filename);
         // Report the error - more information is available in ErrorInfo - use the reference to phpMailer we stole in the hook function
         $this->bootstrap->Log_Event(WPONLINEBACKUP_EVENT_ERROR, __('Failed to send an email containing the backup file. It may be too large to send (%s). Try reducing the size of your backup by adding some exclusions.', 'wponlinebackup') . PHP_EOL . 'PHPMailer: ' . (isset($this->PHPMailer->ErrorInfo) ? $this->PHPMailer->ErrorInfo : 'ErrorInfo unavailable') . PHP_EOL . $error);
         return sprintf(__('Failed to send an email containing the backup file; it may be too large to send (%s).', 'wponlinebackup'), $text_size);
     } else {
         $this->bootstrap->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, __('Successfully emailed the backup.', 'wponlinebackup'));
     }
     // Remove the hook
     remove_action('phpmailer_init', array(&$this, 'phpmailer_init'));
     // Free memory in case it wasn't already
     unset($this->attachment_data);
     unset($this->attachment_filename);
     $this->job['progress'] = 100;
     return true;
 }
 function Commit_Stream()
 {
     // ASSERTION - We have just finalised a stream with End_Stream() and are ready to commit it
     $size = $this->rolling_size;
     // Did we never hit the large file size? If not, just store with normal deflation
     if ($size <= $this->WPOnlineBackup->Get_Setting('file_buffer_size')) {
         // Calculate CRC
         $crc = crc32($this->rolling_buffer);
         // Compress the data
         $this->rolling_buffer = gzdeflate($this->rolling_buffer);
         // Update the compressed data size
         $zlen = strlen($this->rolling_buffer);
         if (($ret = $this->writer->Write($this->rolling_buffer, $zlen)) !== true) {
             return $ret;
         }
         $this->rolling_buffer = false;
         // Already done everything neccessary, move out
         return compact('size', 'crc', 'zlen');
     }
     // Finalize crc if we used hash_init
     if ($this->rolling_hash_ctx !== false) {
         list($inc_crc) = array_values(unpack('N', hash_final($this->rolling_hash_ctx, true)));
         $this->rolling_hash_ctx = false;
         if ($this->rolling_crc !== false) {
             $this->rolling_crc = WPOnlineBackup_Functions::Combine_CRC32($this->rolling_crc, $inc_crc, $this->rolling_hash_len);
         } else {
             $this->rolling_crc = $inc_crc;
         }
     }
     $crc = $this->rolling_crc;
     // Close and delete the buffer file
     $this->rolling_tempfile->CleanUp();
     if ($this->register_temps) {
         $this->WPOnlineBackup->bootstrap->Unregister_Temp($this->rolling_tempfile->Get_File_Path());
     }
     $this->rolling_tempfile = false;
     // Get the deflate stream size
     if (($zlen = @filesize($this->rolling_gzipname)) === false) {
         return OBFW_Exception();
     }
     // Take away header and trailer
     $zlen -= 10 + 8;
     // Start to transfer the compressed data to our own file
     if (($f = @fopen($this->rolling_gzipname, 'rb')) === false) {
         return OBFW_Exception();
     }
     // Skip the header
     if (@fseek($f, 10, SEEK_SET) != 0) {
         $e = OBFW_Exception();
         @fclose($f);
         return $e;
     }
     // Start transferring
     $data = '';
     while (!@feof($f)) {
         // Always get 8 extra bytes
         if (($extract = @fread($f, $this->WPOnlineBackup->Get_Setting('max_block_size') + 8)) === false) {
             $e = OBFW_Exception();
             @fclose($f);
             return $e;
         }
         $data .= $extract;
         // Write all but the last 8 bytes
         $to_write = strlen($data) - 8;
         // Stop if we don't have anything to write
         if ($to_write == 0) {
             break;
         }
         // Write
         if (($ret = $this->writer->Write($data, $to_write)) !== true) {
             @fclose($f);
             return $ret;
         }
         // Leave the last 8 on the buffer
         $data = substr($data, -8);
     }
     // Clean up the tempfile
     @fclose($f);
     return compact('size', 'crc', 'zlen');
 }
示例#6
0
 function Start_Reconstruct()
 {
     // ASSERTION - Haven't already started flushing - we can only Flush OR Reconstruct
     // No reconstruction neccessary, just return true and return the result in Do_Reconstruct()
     if (count($this->rotation) == 0) {
         $this->status = 3;
         return true;
     }
     // Open a file to reconstruct into
     if (($this->rc_file = @fopen($this->prefix . '/' . $this->name . '.rc.php', 'w+b')) === false) {
         return OBFW_Exception();
     }
     $this->status = 2;
     $this->rc_offset = 0;
     // Only write rejection header if we want one on the rc file
     if ($this->rc_header && true !== ($ret = $this->Write_Rejection_Header($this->rc_file))) {
         @fclose($this->rc_file);
         return $ret;
     }
     return true;
 }