<?php

// DO NOT CALL THIS CLASS DIRECTLY. CALL VIA: pb_backupbuddy_destination in bootstrap.php.

class pb_backupbuddy_destination_site {
	
	const TIME_WIGGLE_ROOM = 15;								// Number of seconds to fudge up the time elapsed to give a little wiggle room so we don't accidently hit the edge and time out.
	
	public static $destination_info = array(
		'name'			=>		'Site Instance running BackupBuddy',
		'description'	=>		'Push to or Pull from another instance of this WordPress site running BackupBuddy. Great for rapidly copying a site to and from a development version back and forth with a live site.',
	);
	
	// Default settings. Should be public static for auto-merging.
	public static $default_settings = array(
		'type'			=>		'site',	// MUST MATCH your destination slug.
		'title'			=>		'',		// Required destination field.
		'api_key'		=>		'',
		'max_payload'	=>		'2',	// Max payload in MB to send per chunk. This WILL be read into memory.
		'max_time'		=>		'30',	// Max time in seconds to allow a send to run for.
		'resume_point'	=>		'',		// fseek resume point (via ftell).
	);
	
	private static $_timeStart = 0;
	
	
	/*	send()
	 *	
	 *	Send one or more files.
	 *	
	 *	@param		array			$files		Array of one or more files to send. IMPORTANT: Currently only supports ONE file.
	 *	@return		boolean						True on success, else false.
	 */
	public static function send( $settings = array(), $files = array(), $send_id = '' ) {
		self::$_timeStart = microtime( true );
		
		if ( count( $files ) > 1 ) {
			return 'Error #84545894585: This destination currently only supports one file per send.';
		}
		
		$apiSettings = backupbuddy_core::api_key_to_array( $settings['api_key'] );
		
		$apiURL = $apiSettings['siteurl'];
		$file = $files[0];
		$maxPayload = $settings['max_payload'] * 1024 * 1024; // Convert to bytes.
		
		// Open file for reading.
		if ( FALSE === ( $fs = @fopen( $file, 'r' ) ) ) {
			pb_backupbuddy::status( 'error', 'Error #438934894: Unable to open file `' . $file . '` for reading.' );
			return false;
		}
		
		// If chunked resuming then seek to the correct place in the file.
		if ( ( '' != $settings['resume_point'] ) && ( $settings['resume_point'] > 0 ) ) { // Resuming send of a partially transferred file.
			if ( 0 !== fseek( $fs, $settings['resume_point'] ) ) { // Returns 0 on success.
				pb_backupbuddy::status( 'error', 'Error #327834783: Failed to seek file to resume point `' . $settings['resume_point'] . '` via fseek().' );
				return false;
			}
			$prevPointer = $settings['resume_point'];
		} else { // New file send.
			$size = filesize( $file );
			pb_backupbuddy::status( 'details', 'File size of file to send: ' . pb_backupbuddy::$format->file_size( $size ) );
			if ( $size > $maxPayload ) {
				pb_backupbuddy::status( 'details', 'This file exceeds the maximum per-chunk payload size so will be read in and sent in chunks of ' . $settings['max_payload'] . 'MB totalling ' . ceil( $size / $maxPayload ) . ' chunks.' );
			}
			$prevPointer = 0;
		}
		
		while ( FALSE !== ( $fileData = fgets( $fs, $maxPayload ) ) ) { // Grab one chunk of data at a time.
			
			$response = wp_remote_post( $apiURL, array(
				'method' => 'POST',
				'timeout' => $settings['max_time'],
				'redirection' => 5,
				'httpversion' => '1.0',
				'blocking' => true,
				'headers' => array(),
				'body' => array(
						'backupbuddy_api_key' => $settings['api_key'],
						'backupbuddy_version' => pb_backupbuddy::settings( 'version' ),
						'verb' => 'sendFile',
						
						'filename' => basename( $file ),
						'filedata' => $fileData,
						'crc' => sprintf ( "%u", crc32( $fileData ) ),
						'seekto' => $prevPointer, // Location to seek to before writing this part.
					),
				'cookies' => array()
				)
			);
			if ( is_wp_error( $response ) ) {
				return $response->get_error_message();
			} else {
				if ( null === ( $response = json_decode( $response['body'], true ) ) ) {
					return 'Error #4543664b: Unable to decode json response. Verify remote site API URL, API key, and that the remote site has the API enabled. Return data: `' . htmlentities( $response['body'] ) . '`.';
				} else {
					if ( true !== $response['success'] ) {
						return 'Error #3289379b: API did not report success.';
					} else {
						print_r( $response );
						return true;
					}
				}
			}
			
			if ( FALSE === ( $prevPointer = ftell( $fs ) ) ) {
				pb_backupbuddy::status( 'error', 'Error #438347844: Unable to get ftell pointer of file handle for passing to prevPointer.' );
				@fclose( $fs );
				return false;
			}
			
			// 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...
			if ( ( ( microtime( true ) - $this->time_start ) + self::TIME_WIGGLE_ROOM ) >= $settings['max_time'] ) {
				pb_backupbuddy::status( 'message', 'Approaching limit of available PHP chunking time of `' . $settings['max_time'] . '` sec. Ran for ' . round( microtime( true ) - $this->time_start, 3 ) . ' sec. Proceeding to use chunking.' );
				@fclose( $fs );
				return array( $prevPointer, ( microtime( true ) - $this->time_start ) ); // filepointer location, elapsed time during the import
			} // End if.
			
		} // end while data remains in file.
		
		// Made it this far so completed!
		pb_backupbuddy::status( 'message', 'Finished sending file. Took ' . round( microtime( true ) - $this->time_start, 3 ) . ' seconds this round.' );
		return true;
		
	} // End send().
	
	
	
	/*	test()
	 *	
	 *	function description
	 *	
	 *	@param		array			$settings	Destination settings.
	 *	@return		bool|string					True on success, string error message on failure.
	 */
	public static function test( $settings ) {
		
		/*
		if ( ( $settings['address'] == '' ) || ( $settings['username'] == '' ) || ( $settings['password'] == '' ) ) {
			return __('Missing required input.', 'it-l10n-backupbuddy' );
		}
		*/
		
		// Try sending a file.
		$send_response = pb_backupbuddy_destinations::send( $settings, dirname( dirname( __FILE__ ) ) . '/remote-send-test.php', $send_id = 'TEST-' . pb_backupbuddy::random_string( 12 ) ); // 3rd param true forces clearing of any current uploads.
		if ( false === $send_response ) {
			$send_response = 'Error sending test file to site.';
		} else {
			return true;
		}
		
		
	} // End test().
	
	
} // End class.