access_key = $access_key; $this->secret_key = $secret_key; $this->api_host = $api_host; } /** * Get a list of CloudFront distributions */ public function distributions_list() { $response = $this->request( 'GET', '/2014-11-06/distribution' ); $data = $response['body_array']; $data = $this->fix_array( $data, array( 'Items', 'DistributionSummary' ) ); if ( isset( $data['Items']['DistributionSummary'] ) ) { foreach ( $data['Items']['DistributionSummary'] as $key => $value ) { $data['Items']['DistributionSummary'][$key] = $this->fix_distribution( $data['Items']['DistributionSummary'][$key] ); } } return $data; } /** * Get a list of CloudFront distributions */ public function distribution_get( $id ) { $response = $this->request( 'GET', '/2014-11-06/distribution/' . $id ); $data = $response['body_array']; if ( isset( $data['DistributionConfig'] ) ) $data['DistributionConfig'] = $this->fix_distribution( $data['DistributionConfig'] ); return $data; } /** * $distribution * origin */ public function distribution_create( $distribution ) { if ( !isset( $distribution['CallerReference'] ) ) $distribution['CallerReference'] = rand(); if ( !isset( $distribution['Enabled'] ) ) $distribution['Enabled'] = 'true'; $data = '' . '' . $this->array_to_xml( $distribution ) . ''; $response = $this->request( 'POST', '/2014-11-06/distribution', $data ); $response_data = $response['body_array']; return $response_data; } /** * $distribution * origin */ public function distribution_update( $distribution_id, $distribution ) { $get_response = $this->request( 'GET', '/2014-11-06/distribution/' . $distribution_id ); $get_distribution = $get_response['body_array']; $c = $get_distribution['DistributionConfig']; // update values foreach ( $distribution as $key => $value ) { if ( $key == 'DefaultCacheBehavior' ) { // inherit inner values of that setting too foreach ( $distribution[$key] as $key2 => $value2 ) { $c[$key][$key2] = $value2; } } else { $c[$key] = $value; } } $data = '' . '' . $this->array_to_xml( $c ) . ''; $response = $this->request( 'PUT', '/2014-11-06/distribution/' . $distribution_id . '/config', $data, array( 'If-Match' => $get_response['headers']['etag'] ) ); $data = $response['body_array']; if ( isset( $data['DistributionConfig'] ) ) $data['DistributionConfig'] = $this->fix_distribution( $data['DistributionConfig'] ); return $data; } /** * Invalidate cache */ public function invalidation_create( $distribution_id, $uris ) { $data = '' . '' . '' . '' . count( $uris ) . '' . $this->array_to_xml( array( 'Items' => array( 'Path' => $uris ) ) ) . '' . '' . rand() . '' . ''; $response = $this->request( 'POST', '/2014-11-06/distribution/' . $distribution_id . '/invalidation', $data ); $response_data = $response['body_array']; return $response_data; } /** * Constructor * * @param string $verb Verb * @param string $bucket Bucket name * @param string $uri Object URI * @return mixed */ private function request( $method, $uri = '', $data = '', $headers = array() ) { $url = 'https://' . $this->api_host . $uri; $headers['Host'] = $this->api_host; $headers['x-amz-date'] = gmdate( 'Ymd\THis\Z', time() ); if ( $method == "POST" || $method == "PUT" ) { $headers['Content-Type'] = 'application/xml; charset=utf-8'; $headers['Content-Length'] = strlen( $data ); } else { $data = ''; } $headers['Authorization'] = $this->calculateSignature( $method, $uri, $headers, $data ); // do the request $request = array( 'sslverify' => false, 'headers' => $headers, 'method' => $method, 'body' => $data ); $response = wp_remote_request( $url, $request ); // handle response if ( substr( $response['body'], 0, 5 ) != 'canonicalize_uri( $uri ) . "\n" . '' /* query string */ . "\n" . implode( "\n", $canonical_headers ) . "\n\n" . $signed_headers . "\n" . $payload; $string_to_sign = "AWS4-HMAC-SHA256\n" . $headers['x-amz-date'] . "\n" . $credential_scope . "\n" . hash( 'sha256', $canonical_request ); // Calculate the signing key using a series of derived keys $signingKey = $this->get_signing_key( $short_date, $region, $service, $this->secret_key ); $signature = hash_hmac( 'sha256', $string_to_sign, $signingKey ); return "AWS4-HMAC-SHA256 " . "Credential={$this->access_key}/{$credential_scope}, " . "SignedHeaders={$signed_headers}, Signature={$signature}"; } private function canonicalize_uri( $uri ) { $doubleEncoded = rawurlencode( ltrim( $uri, '/' ) ); return '/' . str_replace( '%2F', '/', $doubleEncoded ); } private function get_signing_key( $short_date, $region, $service, $secretKey ) { $dateKey = hash_hmac( 'sha256', $short_date, 'AWS4' . $secretKey, true ); $regionKey = hash_hmac( 'sha256', $region, $dateKey, true ); $serviceKey = hash_hmac( 'sha256', $service, $regionKey, true ); return hash_hmac( 'sha256', 'aws4_request', $serviceKey, true ); } private function array_to_xml( $a ) { if ( !is_array( $a ) ) return $a; $s = ''; foreach ( $a as $key => $value ) { if ( is_array( $value ) && isset( $value[0] ) ) { // number-indexed array, serialized as list foreach ( $value as $array_item ) { $s .= '<' . $key . '>' . $this->array_to_xml( $array_item ) . ''; } } else { $s .= '<' . $key . '>' . $this->array_to_xml( $value ) . ''; } } return $s; } /** * XML parsing suffers common problem of array recognition * a is accessible via $data['items']['item'] * but * ab is accessible * via $data['items'][$n]['item'] * * by knowing tag we can try to guess that it contains only 1 element * and turn it to array so that it will be always accessible via 2nd way */ private function fix_array( $response, $keys ) { $a = &$response; for ( $n = 0; $n < count( $keys ) - 1; $n++ ) { $key = $keys[$n]; if ( !isset( $a[$key] ) ) break; $a = &$a[$key]; } $last_key = $keys[count( $keys ) - 1]; if ( isset( $a[$last_key] ) ) { if ( !isset( $a[$last_key][0] ) || is_string( $a[$last_key] ) ) { $a[$last_key] = array( $a[$last_key] ); } } return $response; } private function fix_distribution( $data ) { $data = $this->fix_array( $data, array( 'Aliases', 'Items', 'CNAME' ) ); $data = $this->fix_array( $data, array( 'Origins', 'Items', 'Origin' ) ); $data = $this->fix_array( $data, array( 'DefaultCacheBehavior', 'ForwardedValues', 'Headers', 'Items', 'Name' ) ); return $data; } }