WebService.pm 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. # -*- Mode: perl; indent-tabs-mode: nil -*-
  2. #
  3. # The contents of this file are subject to the Mozilla Public
  4. # License Version 1.1 (the "License"); you may not use this file
  5. # except in compliance with the License. You may obtain a copy of
  6. # the License at http://www.mozilla.org/MPL/
  7. #
  8. # Software distributed under the License is distributed on an "AS
  9. # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10. # implied. See the License for the specific language governing
  11. # rights and limitations under the License.
  12. #
  13. # The Original Code is the Bugzilla Bug Tracking System.
  14. #
  15. # Contributor(s): Marc Schumann <wurblzap@gmail.com>
  16. # Max Kanat-Alexander <mkanat@bugzilla.org>
  17. package Bugzilla::WebService;
  18. use strict;
  19. use Bugzilla::WebService::Constants;
  20. use Bugzilla::Util;
  21. use Date::Parse;
  22. sub fail_unimplemented {
  23. my $this = shift;
  24. die SOAP::Fault
  25. ->faultcode(ERROR_UNIMPLEMENTED)
  26. ->faultstring('Service Unimplemented');
  27. }
  28. sub datetime_format {
  29. my ($self, $date_string) = @_;
  30. my $time = str2time($date_string);
  31. my ($sec, $min, $hour, $mday, $mon, $year) = localtime $time;
  32. # This format string was stolen from SOAP::Utils->format_datetime,
  33. # which doesn't work but which has almost the right format string.
  34. my $iso_datetime = sprintf('%d%02d%02dT%02d:%02d:%02d',
  35. $year + 1900, $mon + 1, $mday, $hour, $min, $sec);
  36. return $iso_datetime;
  37. }
  38. sub handle_login {
  39. my ($classes, $action, $uri, $method) = @_;
  40. my $class = $classes->{$uri};
  41. eval "require $class";
  42. return if $class->login_exempt($method);
  43. Bugzilla->login();
  44. # Even though we check for the need to redirect in
  45. # Bugzilla->login() we check here again since Bugzilla->login()
  46. # does not know what the current XMLRPC method is. Therefore
  47. # ssl_require_redirect in Bugzilla->login() will have returned
  48. # false if system was configured to redirect for authenticated
  49. # sessions and the user was not yet logged in.
  50. # So here we pass in the method name to ssl_require_redirect so
  51. # it can then check for the extra case where the method equals
  52. # User.login, which we would then need to redirect if not
  53. # over a secure connection.
  54. my $full_method = $uri . "." . $method;
  55. Bugzilla->cgi->require_https(Bugzilla->params->{'sslbase'})
  56. if ssl_require_redirect($full_method);
  57. return;
  58. }
  59. # For some methods, we shouldn't call Bugzilla->login before we call them
  60. use constant LOGIN_EXEMPT => { };
  61. sub login_exempt {
  62. my ($class, $method) = @_;
  63. return $class->LOGIN_EXEMPT->{$method};
  64. }
  65. 1;
  66. package Bugzilla::WebService::XMLRPC::Transport::HTTP::CGI;
  67. use strict;
  68. eval { require XMLRPC::Transport::HTTP; };
  69. our @ISA = qw(XMLRPC::Transport::HTTP::CGI);
  70. sub initialize {
  71. my $self = shift;
  72. my %retval = $self->SUPER::initialize(@_);
  73. $retval{'serializer'} = Bugzilla::WebService::XMLRPC::Serializer->new;
  74. return %retval;
  75. }
  76. sub make_response {
  77. my $self = shift;
  78. $self->SUPER::make_response(@_);
  79. # XMLRPC::Transport::HTTP::CGI doesn't know about Bugzilla carrying around
  80. # its cookies in Bugzilla::CGI, so we need to copy them over.
  81. foreach (@{Bugzilla->cgi->{'Bugzilla_cookie_list'}}) {
  82. $self->response->headers->push_header('Set-Cookie', $_);
  83. }
  84. }
  85. 1;
  86. # This package exists to fix a UTF-8 bug in SOAP::Lite.
  87. # See http://rt.cpan.org/Public/Bug/Display.html?id=32952.
  88. package Bugzilla::WebService::XMLRPC::Serializer;
  89. use strict;
  90. # We can't use "use base" because XMLRPC::Serializer doesn't return
  91. # a true value.
  92. eval { require XMLRPC::Lite; };
  93. our @ISA = qw(XMLRPC::Serializer);
  94. sub new {
  95. my $class = shift;
  96. my $self = $class->SUPER::new(@_);
  97. # This fixes UTF-8.
  98. $self->{'_typelookup'}->{'base64'} =
  99. [10, sub { !utf8::is_utf8($_[0]) && $_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/},
  100. 'as_base64'];
  101. # This makes arrays work right even though we're a subclass.
  102. # (See http://rt.cpan.org//Ticket/Display.html?id=34514)
  103. $self->{'_encodingStyle'} = '';
  104. return $self;
  105. }
  106. sub as_string {
  107. my $self = shift;
  108. my ($value) = @_;
  109. # Something weird happens with XML::Parser when we have upper-ASCII
  110. # characters encoded as UTF-8, and this fixes it.
  111. utf8::encode($value) if utf8::is_utf8($value)
  112. && $value =~ /^[\x00-\xff]+$/;
  113. return $self->SUPER::as_string($value);
  114. }
  115. 1;
  116. __END__
  117. =head1 NAME
  118. Bugzilla::WebService - The Web Service interface to Bugzilla
  119. =head1 DESCRIPTION
  120. This is the standard API for external programs that want to interact
  121. with Bugzilla. It provides various methods in various modules.
  122. Currently the only method of accessing the API is via XML-RPC. The XML-RPC
  123. standard is described here: L<http://www.xmlrpc.com/spec>
  124. The endpoint for Bugzilla WebServices is the C<xmlrpc.cgi> script in
  125. your Bugzilla installation. For example, if your Bugzilla is at
  126. C<bugzilla.yourdomain.com>, then your XML-RPC client would access the
  127. API via: C<http://bugzilla.yourdomain.com/xmlrpc.cgi>
  128. =head1 CALLING METHODS
  129. Methods are called in the normal XML-RPC fashion. Bugzilla does not currently
  130. implement any extensions to the standard method of XML-RPC method calling.
  131. Methods are grouped into "packages", like C<Bug> for
  132. L<Bugzilla::WebService::Bug>. So, for example,
  133. L<Bugzilla::WebService::Bug/get>, is called as C<Bug.get> in XML-RPC.
  134. =head1 PARAMETERS
  135. In addition to the standard parameter types like C<int>, C<string>, etc.,
  136. XML-RPC has two data structures, a C<< <struct> >> and an C<< <array> >>.
  137. =head2 Structs
  138. In Perl, we call a C<< <struct> >> a "hash" or a "hashref". You may see
  139. us refer to it that way in the API documentation.
  140. In example code, you will see the characters C<{> and C<}> used to represent
  141. the beginning and end of structs.
  142. For example, here's a struct in XML-RPC:
  143. <struct>
  144. <member>
  145. <name>fruit</name>
  146. <value><string>oranges</string></value>
  147. </member>
  148. <member>
  149. <name>vegetable</name>
  150. <value><string>lettuce</string></value>
  151. </member>
  152. </struct>
  153. In our example code in these API docs, that would look like:
  154. { fruit => 'oranges', vegetable => 'lettuce' }
  155. =head2 Arrays
  156. In example code, you will see the characters C<[> and C<]> used to
  157. represent the beginning and end of arrays.
  158. For example, here's an array in XML-RPC:
  159. <array>
  160. <data>
  161. <value><i4>1</i4></value>
  162. <value><i4>2</i4></value>
  163. <value><i4>3</i4></value>
  164. </data>
  165. </array>
  166. In our example code in these API docs, that would look like:
  167. [1, 2, 3]
  168. =head2 How Bugzilla WebService Methods Take Parameters
  169. B<All> Bugzilla WebServices functions take their parameters in
  170. a C<< <struct> >>. Another way of saying this would be: All functions
  171. take a single argument, a C<< <struct> >> that contains all parameters.
  172. The names of the parameters listed in the API docs for each function are
  173. the C<name> element for the struct C<member>s.
  174. =head1 LOGGING IN
  175. You can use L<Bugzilla::WebService::User/login> to log in as a Bugzilla
  176. user. This issues standard HTTP cookies that you must then use in future
  177. calls, so your XML-RPC client must be capable of receiving and transmitting
  178. cookies.
  179. =head1 STABLE, EXPERIMENTAL, and UNSTABLE
  180. Methods are marked B<STABLE> if you can expect their parameters and
  181. return values not to change between versions of Bugzilla. You are
  182. best off always using methods marked B<STABLE>. We may add parameters
  183. and additional items to the return values, but your old code will
  184. always continue to work with any new changes we make. If we ever break
  185. a B<STABLE> interface, we'll post a big notice in the Release Notes,
  186. and it will only happen during a major new release.
  187. Methods (or parts of methods) are marked B<EXPERIMENTAL> if
  188. we I<believe> they will be stable, but there's a slight chance that
  189. small parts will change in the future.
  190. Certain parts of a method's description may be marked as B<UNSTABLE>,
  191. in which case those parts are not guaranteed to stay the same between
  192. Bugzilla versions.
  193. =head1 ERRORS
  194. If a particular webservice call fails, it will throw a standard XML-RPC
  195. error. There will be a numeric error code, and then the description
  196. field will contain descriptive text of the error. Each error that Bugzilla
  197. can throw has a specific code that will not change between versions of
  198. Bugzilla.
  199. The various errors that functions can throw are specified by the
  200. documentation of those functions.
  201. If your code needs to know what error Bugzilla threw, use the numeric
  202. code. Don't try to parse the description, because that may change
  203. from version to version of Bugzilla.
  204. Note that if you display the error to the user in an HTML program, make
  205. sure that you properly escape the error, as it will not be HTML-escaped.
  206. =head2 Transient vs. Fatal Errors
  207. If the error code is a number greater than 0, the error is considered
  208. "transient," which means that it was an error made by the user, not
  209. some problem with Bugzilla itself.
  210. If the error code is a number less than 0, the error is "fatal," which
  211. means that it's some error in Bugzilla itself that probably requires
  212. administrative attention.
  213. Negative numbers and positive numbers don't overlap. That is, if there's
  214. an error 302, there won't be an error -302.
  215. =head2 Unknown Errors
  216. Sometimes a function will throw an error that doesn't have a specific
  217. error code. In this case, the code will be C<-32000> if it's a "fatal"
  218. error, and C<32000> if it's a "transient" error.