state() ) { return new \WP_Error( 'sranywhere_not_connected', __( 'SR Anywhere is not connected.', 'sr-anywhere' ), array( 'status' => 401 ) ); } $token = $connection->is_oauth() ? OAuth::access_token( $connection ) : $connection->token(); if ( null === $token ) { return new \WP_Error( 'sranywhere_not_connected', __( 'SR Anywhere is not connected.', 'sr-anywhere' ), array( 'status' => 401 ) ); } if ( is_wp_error( $token ) ) { return self::not_connected_or( $token ); } $answer = self::get_with_token( $token, $path, $query ); if ( is_wp_error( $answer ) && 401 === ( $answer->get_error_data()['status'] ?? 0 ) ) { if ( $connection->is_oauth() ) { // An access token refused before its time: refresh once, then read again (a 401 may also come from a proxy). $fresh = OAuth::access_token( $connection, true ); if ( is_wp_error( $fresh ) ) { return self::not_connected_or( $fresh ); } $answer = self::get_with_token( $fresh, $path, $query ); if ( ! is_wp_error( $answer ) || 401 !== ( $answer->get_error_data()['status'] ?? 0 ) ) { return $answer; } } Connection::mark( Connection::LEFT_ACCOUNT ); } return $answer; } /** * A refresh failure as the caller sees it: an ended grant is "not connected" (401), everything else stays its error. * * @param \WP_Error $error The refresh's error. * @return \WP_Error */ private static function not_connected_or( \WP_Error $error ) { if ( 'invalid_grant' === $error->get_error_code() || 'sranywhere_no_refresh' === $error->get_error_code() ) { return new \WP_Error( 'sranywhere_not_connected', __( 'SR Anywhere is not connected.', 'sr-anywhere' ), array( 'status' => 401 ) ); } return $error; } /** * A GET request with a given token (to check a pasted key before storing it). * * @param string $token The token. * @param string $path The path. * @param array $query Query arguments. * @return array|\WP_Error */ public static function get_with_token( $token, $path, array $query = array() ) { $url = add_query_arg( array_map( 'rawurlencode', $query ), Config::api_url() . $path ); $response = wp_remote_get( $url, array( 'timeout' => 8, 'redirection' => 0, // never carry the token to another address 'headers' => array( 'Authorization' => 'Bearer ' . $token, 'Accept' => 'application/json', ), ) ); if ( is_wp_error( $response ) ) { return new \WP_Error( 'sranywhere_unreachable', __( 'SR Anywhere could not be reached.', 'sr-anywhere' ), array( 'status' => 0 ) ); } $status = (int) wp_remote_retrieve_response_code( $response ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( 200 === $status && is_array( $body ) ) { return $body; } $codes = array( 401 => 'sranywhere_unauthorised', 403 => 'sranywhere_forbidden', 404 => 'sranywhere_not_found', ); return new \WP_Error( $codes[ $status ] ?? 'sranywhere_unreachable', isset( $body['error']['message'] ) ? sanitize_text_field( $body['error']['message'] ) : __( 'SR Anywhere answered with an error.', 'sr-anywhere' ), array( 'status' => $status ) ); } }