<commit_before># This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

package Bugzilla::WebService::Server::REST;

use 5.10.1;
use strict;
use warnings;

use parent qw(Bugzilla::WebService::Server::JSONRPC);

use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Hook;
use Bugzilla::Util qw(correct_urlbase html_quote);
use Bugzilla::WebService::Constants;
use Bugzilla::WebService::Util qw(taint_data fix_credentials);

# Load resource modules
use Bugzilla::WebService::Server::REST::Resources::Bug;
use Bugzilla::WebService::Server::REST::Resources::Bugzilla;
use Bugzilla::WebService::Server::REST::Resources::Classification;
use Bugzilla::WebService::Server::REST::Resources::Component;
use Bugzilla::WebService::Server::REST::Resources::FlagType;
use Bugzilla::WebService::Server::REST::Resources::Group;
use Bugzilla::WebService::Server::REST::Resources::Product;
use Bugzilla::WebService::Server::REST::Resources::User;
use Bugzilla::WebService::Server::REST::Resources::BugUserLastVisit;

use List::MoreUtils qw(uniq);
use Scalar::Util qw(blessed reftype);
use MIME::Base64 qw(decode_base64);

###########################
# Public Method Overrides #
###########################

sub handle {
    my ($self)  = @_;

    # Determine how the data should be represented. We do this early so
    # errors will also be returned with the proper content type.
    # If no accept header was sent or the content types specified were not
    # matched, we default to the first type in the whitelist.
    $self->content_type($self->_best_content_type(REST_CONTENT_TYPE_WHITELIST()));

    # Using current path information, decide which class/method to
    # use to serve the request. Throw error if no resource was found
    # unless we were looking for OPTIONS
    if (!$self->_find_resource($self->cgi->path_info)) {
        if ($self->request->method eq 'OPTIONS'
            && $self->bz_rest_options)
        {
            my $response = $self->response_header(STATUS_OK, "");
            my $options_string = join(', ', @{ $self->bz_rest_options });
            $response->header('Allow' => $options_string,
                              'Access-Control-Allow-Methods' => $options_string);
            return $self->response($response);
        }

        ThrowUserError("rest_invalid_resource",
                       { path   => $self->cgi->path_info,
                         method => $self->request->method });
    }

    # Dispatch to the proper module
    my $class  = $self->bz_class_name;
    my ($path) = $class =~ /::([^:]+)$/;
    $self->path_info($path);
    delete $self->{dispatch_path};
    $self->dispatch({ $path => $class });

    my $params = $self->_retrieve_json_params;

    fix_credentials($params);

    # Fix includes/excludes for each call
    rest_include_exclude($params);

    # Set callback name if exists
    $self->_bz_callback($params->{'callback'}) if $params->{'callback'};

    Bugzilla->input_params($params);

    # Set the JSON version to 1.1 and the id to the current urlbase
    # also set up the correct handler method
    my $obj = {
        version => '1.1',
        id      => correct_urlbase(),
        method  => $self->bz_method_name,
        params  => $params
    };

    # Execute the handler
    my $result = $self->_handle($obj);

    if (!$self->error_response_header) {
        return $self->response(
            $self->response_header($self->bz_success_code || STATUS_OK, $result));
    }

    $self->response($self->error_response_header);
}

sub response {
    my ($self, $response) = @_;

    # If we have thrown an error, the 'error' key will exist
    # otherwise we use 'result'. JSONRPC returns other data
    # along with the result/error such as version and id which
    # we will strip off for REST calls.
    my $content = $response->content;
    my $json_data = {};
    if ($content) {
        $json_data = $self->json->decode($content);
    }

    my $result = {};
    if (exists $json_data->{error}) {
        $result = $json_data->{error};
        $result->{error} = $self->type('boolean', 1);
        $result->{documentation} = REST_DOC;
        delete $result->{'name'}; # Remove JSONRPCError
    }
    elsif (exists $json_data->{result}) {
        $result = $json_data->{result};
    }

    Bugzilla::Hook::process('webservice_rest_response',
        { rpc => $self, result => \$result, response => $response });

    # Access Control
    $response->header("Access-Control-Allow-Origin", "*");
    $response->header("Access-Control-Allow-Headers", "origin, content-type, accept, x-requested-with");

    # ETag support
    my $etag = $self->bz_etag;
    $self->bz_etag($result) if !$etag;

    # If accessing through web browser, then display in readable format
    if ($self->content_type eq 'text/html') {
        $result = $self->json->pretty->canonical->allow_nonref->encode($result);

        my $template = Bugzilla->template;<commit_msg>Bug 1152319: calling /rest/logout results in an internal error and a malformed response to the caller r=glob,a=glob<commit_after># This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

package Bugzilla::WebService::Server::REST;

use 5.10.1;
use strict;
use warnings;

use parent qw(Bugzilla::WebService::Server::JSONRPC);

use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Hook;
use Bugzilla::Util qw(correct_urlbase html_quote);
use Bugzilla::WebService::Constants;
use Bugzilla::WebService::Util qw(taint_data fix_credentials);

# Load resource modules
use Bugzilla::WebService::Server::REST::Resources::Bug;
use Bugzilla::WebService::Server::REST::Resources::Bugzilla;
use Bugzilla::WebService::Server::REST::Resources::Classification;
use Bugzilla::WebService::Server::REST::Resources::Component;
use Bugzilla::WebService::Server::REST::Resources::FlagType;
use Bugzilla::WebService::Server::REST::Resources::Group;
use Bugzilla::WebService::Server::REST::Resources::Product;
use Bugzilla::WebService::Server::REST::Resources::User;
use Bugzilla::WebService::Server::REST::Resources::BugUserLastVisit;

use List::MoreUtils qw(uniq);
use Scalar::Util qw(blessed reftype);
use MIME::Base64 qw(decode_base64);

###########################
# Public Method Overrides #
###########################

sub handle {
    my ($self)  = @_;

    # Determine how the data should be represented. We do this early so
    # errors will also be returned with the proper content type.
    # If no accept header was sent or the content types specified were not
    # matched, we default to the first type in the whitelist.
    $self->content_type($self->_best_content_type(REST_CONTENT_TYPE_WHITELIST()));

    # Using current path information, decide which class/method to
    # use to serve the request. Throw error if no resource was found
    # unless we were looking for OPTIONS
    if (!$self->_find_resource($self->cgi->path_info)) {
        if ($self->request->method eq 'OPTIONS'
            && $self->bz_rest_options)
        {
            my $response = $self->response_header(STATUS_OK, "");
            my $options_string = join(', ', @{ $self->bz_rest_options });
            $response->header('Allow' => $options_string,
                              'Access-Control-Allow-Methods' => $options_string);
            return $self->response($response);
        }

        ThrowUserError("rest_invalid_resource",
                       { path   => $self->cgi->path_info,
                         method => $self->request->method });
    }

    # Dispatch to the proper module
    my $class  = $self->bz_class_name;
    my ($path) = $class =~ /::([^:]+)$/;
    $self->path_info($path);
    delete $self->{dispatch_path};
    $self->dispatch({ $path => $class });

    my $params = $self->_retrieve_json_params;

    fix_credentials($params);

    # Fix includes/excludes for each call
    rest_include_exclude($params);

    # Set callback name if exists
    $self->_bz_callback($params->{'callback'}) if $params->{'callback'};

    Bugzilla->input_params($params);

    # Set the JSON version to 1.1 and the id to the current urlbase
    # also set up the correct handler method
    my $obj = {
        version => '1.1',
        id      => correct_urlbase(),
        method  => $self->bz_method_name,
        params  => $params
    };

    # Execute the handler
    my $result = $self->_handle($obj);

    if (!$self->error_response_header) {
        return $self->response(
            $self->response_header($self->bz_success_code || STATUS_OK, $result));
    }

    $self->response($self->error_response_header);
}

sub response {
    my ($self, $response) = @_;

    # If we have thrown an error, the 'error' key will exist
    # otherwise we use 'result'. JSONRPC returns other data
    # along with the result/error such as version and id which
    # we will strip off for REST calls.
    my $content = $response->content;
    my $json_data = {};
    if ($content) {
        $json_data = $self->json->decode($content);
    }

    my $result = {};
    if (exists $json_data->{error}) {
        $result = $json_data->{error};
        $result->{error} = $self->type('boolean', 1);
        $result->{documentation} = REST_DOC;
        delete $result->{'name'}; # Remove JSONRPCError
    }
    elsif (exists $json_data->{result}) {
        $result = $json_data->{result};
    }

    # The result needs to be a valid JSON data structure
    # and not a undefined or scalar value.
    if (!ref $result
        || blessed($result)
        || ref $result ne 'HASH'
        || ref $result ne 'ARRAY')
    {
        $result = { result => $result };
    }

    Bugzilla::Hook::process('webservice_rest_response',
        { rpc => $self, result => \$result, response => $response });

    # Access Control
    $response->header("Access-Control-Allow-Origin", "*");
    $response->header("Access-Control-Allow-Headers", "origin, content-type, accept, x-requested-with");

    # ETag support
    my $etag = $self->bz_etag;
    $self->bz_etag($result) if !$etag;

    # If accessing through web browser, then display in readable format
    if ($self->content_type eq 'text/html') {
        $result = $self->json->pretty->canonical->allow_nonref->encode($result);

        my $template = Bugzilla->template;
{'fraction_non_alphanumeric': 0.14942849177585726, 'fraction_numerical': 0.004181767493727348, 'mean_word_length': 3.410655737704918, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 138, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2117031', 'n_tokens_mistral': 3328, 'n_tokens_neox': 3135, 'n_words': 863}