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::Constants;
9
10use 5.10.1;
11use strict;
12use warnings;
13
14use parent qw(Exporter);
15
16our @EXPORT = qw(
17    WS_ERROR_CODE
18
19    STATUS_OK
20    STATUS_CREATED
21    STATUS_ACCEPTED
22    STATUS_NO_CONTENT
23    STATUS_MULTIPLE_CHOICES
24    STATUS_BAD_REQUEST
25    STATUS_NOT_FOUND
26    STATUS_GONE
27    REST_STATUS_CODE_MAP
28
29    ERROR_UNKNOWN_FATAL
30    ERROR_UNKNOWN_TRANSIENT
31
32    XMLRPC_CONTENT_TYPE_WHITELIST
33    REST_CONTENT_TYPE_WHITELIST
34
35    WS_DISPATCH
36);
37
38# This maps the error names in global/*-error.html.tmpl to numbers.
39# Generally, transient errors should have a number above 0, and
40# fatal errors should have a number below 0.
41#
42# This hash should generally contain any error that could be thrown
43# by the WebService interface. If it's extremely unlikely that the
44# error could be thrown (like some CodeErrors), it doesn't have to
45# be listed here.
46#
47# "Transient" means "If you resubmit that request with different data,
48# it may work."
49#
50# "Fatal" means, "There's something wrong with Bugzilla, probably
51# something an administrator would have to fix."
52#
53# NOTE: Numbers must never be recycled. If you remove a number, leave a
54# comment that it was retired. Also, if an error changes its name, you'll
55# have to fix it here.
56use constant WS_ERROR_CODE => {
57    # Generic errors (Bugzilla::Object and others) are 50-99.
58    object_not_specified        => 50,
59    reassign_to_empty           => 50,
60    param_required              => 50,
61    params_required             => 50,
62    undefined_field             => 50,
63    object_does_not_exist       => 51,
64    param_must_be_numeric       => 52,
65    number_not_numeric          => 52,
66    param_invalid               => 53,
67    number_too_large            => 54,
68    number_too_small            => 55,
69    illegal_date                => 56,
70    param_integer_required      => 57,
71    param_scalar_array_required => 58,
72    # Bug errors usually occupy the 100-200 range.
73    improper_bug_id_field_value => 100,
74    bug_id_does_not_exist       => 101,
75    bug_access_denied           => 102,
76    bug_access_query            => 102,
77    # These all mean "invalid alias"
78    alias_too_long           => 103,
79    alias_in_use             => 103,
80    alias_is_numeric         => 103,
81    alias_has_comma_or_space => 103,
82    multiple_alias_not_allowed => 103,
83    # Misc. bug field errors
84    illegal_field => 104,
85    freetext_too_long => 104,
86    # Component errors
87    require_component         => 105,
88    component_name_too_long   => 105,
89    product_unknown_component => 105,
90    # Invalid Product
91    no_products         => 106,
92    entry_access_denied => 106,
93    product_access_denied => 106,
94    product_disabled    => 106,
95    # Invalid Summary
96    require_summary => 107,
97    # Invalid field name
98    invalid_field_name => 108,
99    # Not authorized to edit the bug
100    product_edit_denied => 109,
101    # Comment-related errors
102    comment_is_private => 110,
103    comment_id_invalid => 111,
104    comment_too_long => 114,
105    comment_invalid_isprivate => 117,
106    # Comment tagging
107    comment_tag_disabled => 125,
108    comment_tag_invalid => 126,
109    comment_tag_too_long => 127,
110    comment_tag_too_short => 128,
111    # See Also errors
112    bug_url_invalid => 112,
113    bug_url_too_long => 112,
114    # Insidergroup Errors
115    user_not_insider => 113,
116    # Note: 114 is above in the Comment-related section.
117    # Bug update errors
118    illegal_change => 115,
119    # Dependency errors
120    dependency_loop_single => 116,
121    dependency_loop_multi  => 116,
122    # Note: 117 is above in the Comment-related section.
123    # Dup errors
124    dupe_loop_detected => 118,
125    dupe_id_required => 119,
126    # Bug-related group errors
127    group_invalid_removal => 120,
128    group_restriction_not_allowed => 120,
129    # Status/Resolution errors
130    missing_resolution => 121,
131    resolution_not_allowed => 122,
132    illegal_bug_status_transition => 123,
133    # Flag errors
134    flag_status_invalid => 129,
135    flag_update_denied => 130,
136    flag_type_requestee_disabled => 131,
137    flag_not_unique => 132,
138    flag_type_not_unique => 133,
139    flag_type_inactive => 134,
140
141    # Authentication errors are usually 300-400.
142    invalid_login_or_password => 300,
143    account_disabled             => 301,
144    auth_invalid_email           => 302,
145    extern_id_conflict           => -303,
146    auth_failure                 => 304,
147    password_too_short           => 305,
148    password_not_complex         => 305,
149    api_key_not_valid            => 306,
150    api_key_revoked              => 306,
151    auth_invalid_token           => 307,
152
153    # Except, historically, AUTH_NODATA, which is 410.
154    login_required               => 410,
155
156    # User errors are 500-600.
157    account_exists        => 500,
158    illegal_email_address => 501,
159    auth_cant_create_account    => 501,
160    account_creation_disabled   => 501,
161    account_creation_restricted => 501,
162    password_too_short    => 502,
163    # Error 503 password_too_long no longer exists.
164    invalid_username      => 504,
165    # This is from strict_isolation, but it also basically means
166    # "invalid user."
167    invalid_user_group    => 504,
168    user_access_by_id_denied    => 505,
169    user_access_by_match_denied => 505,
170
171    # Attachment errors are 600-700.
172    file_too_large         => 600,
173    invalid_content_type   => 601,
174    # Error 602 attachment_illegal_url no longer exists.
175    file_not_specified     => 603,
176    missing_attachment_description => 604,
177    # Error 605 attachment_url_disabled no longer exists.
178    zero_length_file       => 606,
179
180    # Product erros are 700-800
181    product_blank_name => 700,
182    product_name_too_long => 701,
183    product_name_already_in_use => 702,
184    product_name_diff_in_case => 702,
185    product_must_have_description => 703,
186    product_must_have_version => 704,
187    product_must_define_defaultmilestone => 705,
188
189    # Group errors are 800-900
190    empty_group_name => 800,
191    group_exists => 801,
192    empty_group_description => 802,
193    invalid_regexp => 803,
194    invalid_group_name => 804,
195    group_cannot_view => 805,
196
197    # Classification errors are 900-1000
198    auth_classification_not_enabled => 900,
199
200    # Search errors are 1000-1100
201    buglist_parameters_required => 1000,
202
203    # Flag type errors are 1100-1200
204    flag_type_name_invalid        => 1101,
205    flag_type_description_invalid => 1102,
206    flag_type_cc_list_invalid     => 1103,
207    flag_type_sortkey_invalid     => 1104,
208    flag_type_not_editable        => 1105,
209
210    # Component errors are 1200-1300
211    component_already_exists => 1200,
212    component_is_last        => 1201,
213    component_has_bugs       => 1202,
214
215    # Errors thrown by the WebService itself. The ones that are negative
216    # conform to http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
217    xmlrpc_invalid_value => -32600,
218    unknown_method       => -32601,
219    json_rpc_post_only   => 32610,
220    json_rpc_invalid_callback => 32611,
221    xmlrpc_illegal_content_type   => 32612,
222    json_rpc_illegal_content_type => 32613,
223    rest_invalid_resource         => 32614,
224};
225
226# RESTful webservices use the http status code
227# to describe whether a call was successful or
228# to describe the type of error that occurred.
229use constant STATUS_OK               => 200;
230use constant STATUS_CREATED          => 201;
231use constant STATUS_ACCEPTED         => 202;
232use constant STATUS_NO_CONTENT       => 204;
233use constant STATUS_MULTIPLE_CHOICES => 300;
234use constant STATUS_BAD_REQUEST      => 400;
235use constant STATUS_NOT_AUTHORIZED   => 401;
236use constant STATUS_NOT_FOUND        => 404;
237use constant STATUS_GONE             => 410;
238
239# The integer value is the error code above returned by
240# the related webvservice call. We choose the appropriate
241# http status code based on the error code or use the
242# default STATUS_BAD_REQUEST.
243sub REST_STATUS_CODE_MAP {
244    my $status_code_map = {
245        51       => STATUS_NOT_FOUND,
246        101      => STATUS_NOT_FOUND,
247        102      => STATUS_NOT_AUTHORIZED,
248        106      => STATUS_NOT_AUTHORIZED,
249        109      => STATUS_NOT_AUTHORIZED,
250        110      => STATUS_NOT_AUTHORIZED,
251        113      => STATUS_NOT_AUTHORIZED,
252        115      => STATUS_NOT_AUTHORIZED,
253        120      => STATUS_NOT_AUTHORIZED,
254        300      => STATUS_NOT_AUTHORIZED,
255        301      => STATUS_NOT_AUTHORIZED,
256        302      => STATUS_NOT_AUTHORIZED,
257        303      => STATUS_NOT_AUTHORIZED,
258        304      => STATUS_NOT_AUTHORIZED,
259        410      => STATUS_NOT_AUTHORIZED,
260        504      => STATUS_NOT_AUTHORIZED,
261        505      => STATUS_NOT_AUTHORIZED,
262        32614    => STATUS_NOT_FOUND,
263        _default => STATUS_BAD_REQUEST
264    };
265
266    Bugzilla::Hook::process('webservice_status_code_map',
267        { status_code_map => $status_code_map });
268
269    return $status_code_map;
270};
271
272# These are the fallback defaults for errors not in ERROR_CODE.
273use constant ERROR_UNKNOWN_FATAL     => -32000;
274use constant ERROR_UNKNOWN_TRANSIENT => 32000;
275
276use constant ERROR_GENERAL       => 999;
277
278use constant XMLRPC_CONTENT_TYPE_WHITELIST => qw(
279    text/xml
280    application/xml
281);
282
283# The first content type specified is used as the default.
284use constant REST_CONTENT_TYPE_WHITELIST => qw(
285    application/json
286    application/javascript
287    text/javascript
288    text/html
289);
290
291sub WS_DISPATCH {
292    # We "require" here instead of "use" above to avoid a dependency loop.
293    require Bugzilla::Hook;
294    my %hook_dispatch;
295    Bugzilla::Hook::process('webservice', { dispatch => \%hook_dispatch });
296
297    my $dispatch = {
298        'Bugzilla'         => 'Bugzilla::WebService::Bugzilla',
299        'Bug'              => 'Bugzilla::WebService::Bug',
300        'Classification'   => 'Bugzilla::WebService::Classification',
301        'Component'        => 'Bugzilla::WebService::Component',
302        'FlagType'         => 'Bugzilla::WebService::FlagType',
303        'Group'            => 'Bugzilla::WebService::Group',
304        'Product'          => 'Bugzilla::WebService::Product',
305        'User'             => 'Bugzilla::WebService::User',
306        'BugUserLastVisit' => 'Bugzilla::WebService::BugUserLastVisit',
307        %hook_dispatch
308    };
309    return $dispatch;
310};
311
3121;
313
314=head1 B<Methods in need of POD>
315
316=over
317
318=item REST_STATUS_CODE_MAP
319
320=item WS_DISPATCH
321
322=back
323