1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4#
5# This Source Code Form is "Incompatible With Secondary Licenses", as
6# defined by the Mozilla Public License, v. 2.0.
7
8package Bugzilla::WebService::Server;
9
10use 5.10.1;
11use strict;
12use warnings;
13
14use Bugzilla::Error;
15use Bugzilla::Util qw(datetime_from);
16
17use Scalar::Util qw(blessed);
18use Digest::MD5 qw(md5_base64);
19
20use Storable qw(freeze);
21
22sub handle_login {
23    my ($self, $class, $method, $full_method) = @_;
24    # Throw error if the supplied class does not exist or the method is private
25    ThrowCodeError('unknown_method', {method => $full_method}) if (!$class or $method =~ /^_/);
26
27    eval "require $class";
28    ThrowCodeError('unknown_method', {method => $full_method}) if $@;
29    return if ($class->login_exempt($method)
30               and !defined Bugzilla->input_params->{Bugzilla_login});
31    Bugzilla->login();
32
33    Bugzilla::Hook::process(
34        'webservice_before_call',
35        { 'method'  => $method, full_method => $full_method });
36}
37
38sub datetime_format_inbound {
39    my ($self, $time) = @_;
40
41    my $converted = datetime_from($time, Bugzilla->local_timezone);
42    if (!defined $converted) {
43        ThrowUserError('illegal_date', { date => $time });
44    }
45    $time = $converted->ymd() . ' ' . $converted->hms();
46    return $time
47}
48
49sub datetime_format_outbound {
50    my ($self, $date) = @_;
51
52    return undef if (!defined $date or $date eq '');
53
54    my $time = $date;
55    if (blessed($date)) {
56        # We expect this to mean we were sent a datetime object
57        $time->set_time_zone('UTC');
58    } else {
59        # We always send our time in UTC, for consistency.
60        # passed in value is likely a string, create a datetime object
61        $time = datetime_from($date, 'UTC');
62    }
63    return $time->iso8601();
64}
65
66# ETag support
67sub bz_etag {
68    my ($self, $data) = @_;
69    my $cache = Bugzilla->request_cache;
70    if (defined $data) {
71        # Serialize the data if passed a reference
72        local $Storable::canonical = 1;
73        $data = freeze($data) if ref $data;
74
75        # Wide characters cause md5_base64() to die.
76        utf8::encode($data) if utf8::is_utf8($data);
77
78        # Append content_type to the end of the data
79        # string as we want the etag to be unique to
80        # the content_type. We do not need this for
81        # XMLRPC as text/xml is always returned.
82        if (blessed($self) && $self->can('content_type')) {
83            $data .= $self->content_type if $self->content_type;
84        }
85
86        $cache->{'bz_etag'} = md5_base64($data);
87    }
88    return $cache->{'bz_etag'};
89}
90
911;
92
93=head1 NAME
94
95Bugzilla::WebService::Server - Base server class for the WebService API
96
97=head1 DESCRIPTION
98
99Bugzilla::WebService::Server is the base class for the individual WebService API
100servers such as XMLRPC, JSONRPC, and REST. You never actually create a
101Bugzilla::WebService::Server directly, you only make subclasses of it.
102
103=head1 FUNCTIONS
104
105=over
106
107=item C<bz_etag>
108
109This function is used to store an ETag value that will be used when returning
110the data by the different API server modules such as XMLRPC, or REST. The individual
111webservice methods can also set the value earlier in the process if needed such as
112before a unique update token is added. If a value is not set earlier, an etag will
113automatically be created using the returned data except in some cases when an error
114has occurred.
115
116=back
117
118=head1 SEE ALSO
119
120L<Bugzilla::WebService::Server::XMLRPC|XMLRPC>, L<Bugzilla::WebService::Server::JSONRPC|JSONRPC>,
121and L<Bugzilla::WebService::Server::REST|REST>.
122
123=head1 B<Methods in need of POD>
124
125=over
126
127=item handle_login
128
129=item datetime_format_outbound
130
131=item datetime_format_inbound
132
133=back
134