376 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			376 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace W3TC;
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * CloudFlare API
 | |
|  */
 | |
| class Extension_CloudFlare_Api {
 | |
| 	static private $_root_uri = 'https://api.cloudflare.com/client/v4';
 | |
| 
 | |
| 	private $_email;
 | |
| 	private $_key;
 | |
| 	private $_zone_id;
 | |
| 	private $_timelimit_api_request;
 | |
| 
 | |
| 
 | |
| 
 | |
| 	function __construct( $config ) {
 | |
| 		$this->_email = $config['email'];
 | |
| 		$this->_key = $config['key'];
 | |
| 		$this->_zone_id =
 | |
| 			( isset( $config['zone_id'] ) ? $config['zone_id'] : '' );
 | |
| 
 | |
| 		if ( !isset( $config['timelimit_api_request'] ) ||
 | |
| 			$config['timelimit_api_request'] < 1 )
 | |
| 			$this->_timelimit_api_request = 30;
 | |
| 		else
 | |
| 			$this->_timelimit_api_request = $config['timelimit_api_request'];
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 
 | |
| 	/**
 | |
| 	 * Makes external event request
 | |
| 	 *
 | |
| 	 * @param string  $type
 | |
| 	 * @param string  $value
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function external_event( $type, $value ) {
 | |
| 		$url = sprintf( 'https://www.cloudflare.com/ajax/external-event.html?' .
 | |
| 			'u=%s&tkn=%s&evnt_t=%s&evnt_v=%s',
 | |
| 			urlencode( $this->_email ), urlencode( $this->_key ),
 | |
| 			urlencode( $type ), urlencode( $value ) );
 | |
| 		$response = Util_Http::get( $url );
 | |
| 
 | |
| 		if ( !is_wp_error( $response ) ) {
 | |
| 			return json_decode( $response['body'] );
 | |
| 		}
 | |
| 
 | |
| 		return null;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/**
 | |
| 	 * Check
 | |
| 	 *
 | |
| 	 * @throws Util_WpFile_FilesystemOperationException
 | |
| 	 * @throws FileOperationException
 | |
| 	 */
 | |
| 	public function get_ip_ranges() {
 | |
| 		$data = array();
 | |
| 		$response = Util_Http::get( 'https://www.cloudflare.com/ips-v4' );
 | |
| 
 | |
| 		if ( !is_wp_error( $response ) ) {
 | |
| 			$ip4_data = $response['body'];
 | |
| 			$ip4_data = explode( "\n", $ip4_data );
 | |
| 			$data['ip4'] = $ip4_data;
 | |
| 		}
 | |
| 		$response = Util_Http::get( 'https://www.cloudflare.com/ips-v6' );
 | |
| 		if ( !is_wp_error( $response ) ) {
 | |
| 			$ip6_data = $response['body'];
 | |
| 			$ip6_data = explode( "\n", $ip6_data );
 | |
| 			$data['ip6'] = $ip6_data;
 | |
| 		}
 | |
| 
 | |
| 		return $data;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 
 | |
| 	public function zones($page = 1) {
 | |
| 		return $this->_wp_remote_request_with_meta( 'GET',
 | |
| 			self::$_root_uri . '/zones?page=' . urlencode($page) );
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 
 | |
| 	public function zone($id) {
 | |
| 		$a = $this->_wp_remote_request( 'GET',
 | |
| 			self::$_root_uri . '/zones/' . $id );
 | |
| 
 | |
| 		return $a;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 
 | |
| 	public function zone_settings() {
 | |
| 		$a = $this->_wp_remote_request( 'GET',
 | |
| 			self::$_root_uri . '/zones/' . $this->_zone_id . '/settings' );
 | |
| 
 | |
| 		$by_id = array();
 | |
| 		foreach ( $a as $i ) {
 | |
| 			$by_id[$i['id']] = $i;
 | |
| 		}
 | |
| 
 | |
| 		return $by_id;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 
 | |
| 	public function zone_setting_set( $name, $value ) {
 | |
| 		// Convert numeric values to the integer type.
 | |
| 		if ( is_numeric( $value ) ) {
 | |
| 			$value = intval( $value );
 | |
| 		}
 | |
| 
 | |
| 		return $this->_wp_remote_request( 'PATCH',
 | |
| 			self::$_root_uri . '/zones/' . $this->_zone_id . '/settings/' . $name,
 | |
| 			json_encode( array( 'value' => $value ) ) );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Prepares the analytics dashboard widget query to be performed via the CF GraphQL API
 | |
| 	 *
 | |
| 	 * @throws Exception
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function analytics_dashboard( $start, $end, $type = 'day' ) {
 | |
| 		$dataset = 'httpRequests1dGroups';
 | |
| 		$datetime_filter = 'date';
 | |
| 
 | |
| 		if($type === 'hour') {
 | |
| 			$dataset = 'httpRequests1hGroups';
 | |
| 			$datetime_filter = 'datetime';
 | |
| 		}
 | |
| 
 | |
| 		return $this->_wp_remote_request_graphql( 'POST',
 | |
| 			self::$_root_uri . '/graphql',
 | |
| 			"{ \"query\": \"query {
 | |
| 				viewer {
 | |
| 					zones(filter: {zoneTag: \\\"" . $this->_zone_id . "\\\"}) {
 | |
| 						" . $dataset . "(
 | |
| 							orderBy: [" . $datetime_filter . "_ASC],
 | |
| 							limit: 100,
 | |
| 							filter: {
 | |
| 								" . $datetime_filter . "_geq: \\\"" . $start . "\\\",
 | |
| 								" . $datetime_filter . "_lt: \\\"" . $end . "\\\"
 | |
| 							}
 | |
| 						) {
 | |
| 							dimensions {
 | |
| 								" . $datetime_filter . "
 | |
| 							}
 | |
| 							sum {
 | |
| 								bytes
 | |
| 								cachedBytes
 | |
| 								cachedRequests
 | |
| 								pageViews
 | |
| 								requests
 | |
| 								threats
 | |
| 							}
 | |
| 							uniq {
 | |
| 								uniques
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}\"}"
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	public function purge() {
 | |
| 		return $this->_wp_remote_request( 'DELETE',
 | |
| 			self::$_root_uri . '/zones/' . $this->_zone_id . '/purge_cache',
 | |
| 			'{"purge_everything":true}' );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Performs the CF API request
 | |
| 	 *
 | |
| 	 * @throws Exception
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function _wp_remote_request( $method, $url, $body = array() ) {
 | |
| 		if ( empty( $this->_email ) || empty( $this->_key ) ) {
 | |
| 			throw new \Exception('Not authenticated');
 | |
| 		}
 | |
| 
 | |
| 		$headers = $this->_generate_wp_remote_request_headers();
 | |
| 
 | |
| 		$result = wp_remote_request( $url, array(
 | |
| 				'method' => $method,
 | |
| 				'headers' => $headers,
 | |
| 				'timeout' => $this->_timelimit_api_request,
 | |
| 				'body' => $body
 | |
| 			) );
 | |
| 
 | |
| 		if ( is_wp_error( $result ) ) {
 | |
| 			throw new \Exception( 'Failed to reach API endpoint' );
 | |
| 		}
 | |
| 
 | |
| 		$response_json = @json_decode( $result['body'], true );
 | |
| 		if ( is_null( $response_json ) || !isset( $response_json['success'] ) ) {
 | |
| 			throw new \Exception(
 | |
| 				'Failed to reach API endpoint, got unexpected response ' .
 | |
| 				str_replace( '<', '.', str_replace( '>', '.', $result['body'] ) ) );
 | |
| 		}
 | |
| 
 | |
| 		if ( !$response_json['success'] ) {
 | |
| 			$errors = array();
 | |
| 
 | |
| 			if ( isset( $response_json['errors'] ) ) {
 | |
| 				foreach ( $response_json['errors'] as $e ) {
 | |
| 					if ( !empty( $e['message'] ) )
 | |
| 						$errors[] = $e['message'];
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if ( empty( $errors ) )
 | |
| 				$errors[] = 'Request failed';
 | |
| 
 | |
| 			throw new \Exception( implode( ', ', $errors ) );
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $response_json['result'] ) ) {
 | |
| 			return $response_json['result'];
 | |
| 		}
 | |
| 
 | |
| 		return array();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Performs the CF API request with meta
 | |
| 	 *
 | |
| 	 * @throws Exception
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function _wp_remote_request_with_meta( $method, $url, $body = array() ) {
 | |
| 		if ( empty( $this->_email ) || empty( $this->_key ) ) {
 | |
| 			throw new \Exception('Not authenticated');
 | |
| 		}
 | |
| 
 | |
| 		$headers = $this->_generate_wp_remote_request_headers();
 | |
| 
 | |
| 		$result = wp_remote_request( $url, array(
 | |
| 				'method' => $method,
 | |
| 				'headers' => $headers,
 | |
| 				'timeout' => $this->_timelimit_api_request,
 | |
| 				'body' => $body
 | |
| 			) );
 | |
| 
 | |
| 		if ( is_wp_error( $result ) )
 | |
| 			throw new \Exception( 'Failed to reach API endpoint' );
 | |
| 
 | |
| 		$response_json = @json_decode( $result['body'], true );
 | |
| 		if ( is_null( $response_json ) || !isset( $response_json['success'] ) ) {
 | |
| 			throw new \Exception(
 | |
| 				'Failed to reach API endpoint, got unexpected response ' .
 | |
| 				$result['body'] );
 | |
| 		}
 | |
| 
 | |
| 		if ( !$response_json['success'] ) {
 | |
| 			$errors = array();
 | |
| 
 | |
| 			if ( isset( $response_json['errors'] ) ) {
 | |
| 				foreach ( $response_json['errors'] as $e ) {
 | |
| 				    if ( !empty( $e['message'] ) )
 | |
| 				         $errors[] = $e['message'];
 | |
| 				}
 | |
| 			}
 | |
| 			if ( empty( $errors ) )
 | |
| 				$errors[] = 'Request failed';
 | |
| 
 | |
| 			throw new \Exception( implode( ', ', $errors ) );
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $response_json['result'] ) ) {
 | |
| 			return $response_json;
 | |
| 		}
 | |
| 
 | |
| 		return array();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Performs the CF GraphQL API request
 | |
| 	 *
 | |
| 	 * @throws Exception
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function _wp_remote_request_graphql( $method, $url, $body ) {
 | |
| 		if ( empty( $this->_email ) || empty( $this->_key ) ) {
 | |
| 			throw new \Exception('Not authenticated');
 | |
| 		}
 | |
| 
 | |
| 		$headers = $this->_generate_wp_remote_request_headers();
 | |
| 
 | |
| 		$body = preg_replace( '/\s\s+/', ' ', $body );
 | |
| 
 | |
| 		$result = wp_remote_request( $url, array(
 | |
| 				'method' => $method,
 | |
| 				'headers' => $headers,
 | |
| 				'timeout' => $this->_timelimit_api_request,
 | |
| 				'body' => $body
 | |
| 			) );
 | |
| 
 | |
| 		if ( is_wp_error( $result ) ) {
 | |
| 			throw new \Exception( 'Failed to reach API endpoint' );
 | |
| 		}
 | |
| 
 | |
| 		$response_json = @json_decode( $result['body'], true );
 | |
| 		if ( is_null( $response_json ) ) {
 | |
| 			throw new \Exception(
 | |
| 				'Failed to reach API endpoint, got unexpected response ' .
 | |
| 				str_replace( '<', '.', str_replace( '>', '.', $result['body'] ) ) );
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $response_json['errors'] ) ) {
 | |
| 			$errors = array();
 | |
| 
 | |
| 			foreach ( $response_json['errors'] as $e ) {
 | |
| 				if ( !empty( $e['message'] ) )
 | |
| 					$errors[] = $e['message'];
 | |
| 			}
 | |
| 
 | |
| 			if ( empty( $errors ) )
 | |
| 				$errors[] = 'Request failed';
 | |
| 
 | |
| 			throw new \Exception( implode( ', ', $errors ) );
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $response_json['data'] ) ) {
 | |
| 			return $response_json['data'];
 | |
| 		}
 | |
| 
 | |
| 		return array();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Generates the appropriate request headers for CF API requests based on token or global key presence
 | |
| 	 *
 | |
| 	 * @since 2.2.1
 | |
| 	 *
 | |
| 	 * @throws Exception
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	private function _generate_wp_remote_request_headers() {
 | |
| 		if ( empty( $this->_email ) || empty( $this->_key ) ) {
 | |
| 			throw new \Exception('Missing authentication email and/or API token / global key');
 | |
| 		}
 | |
| 
 | |
| 		$headers = array();
 | |
| 
 | |
| 		// CF API Token
 | |
| 		if( strlen( $this->_key ) == 40 ) {
 | |
| 			$headers = array(
 | |
| 				'Content-Type' => 'application/json',
 | |
| 				'Authorization' => 'Bearer ' . $this->_key
 | |
| 			);
 | |
| 		}
 | |
| 		// CF Legacy API Global Key
 | |
| 		else if ( strlen( $this->_key ) == 37 ) {
 | |
| 			$headers = array(
 | |
| 				'Content-Type' => 'application/json',
 | |
| 				'X-Auth-Key' => $this->_key,
 | |
| 				'X-Auth-Email' => $this->_email
 | |
| 			);
 | |
| 		}
 | |
| 		else {
 | |
| 			throw new \Exception('Improper API token / global key length');
 | |
| 		}
 | |
| 
 | |
| 		return $headers;
 | |
| 	}
 | |
| }
 |