aboutsummaryrefslogtreecommitdiffstats
path: root/Bugzilla/WebService
diff options
context:
space:
mode:
authormkanat%bugzilla.org <>2009-02-11 20:23:27 +0000
committermkanat%bugzilla.org <>2009-02-11 20:23:27 +0000
commit6a10905af0af5b3436a344cf9181d29fcea4083a (patch)
tree1f23d362b29985e644bb36649b976d3cb44d0ce0 /Bugzilla/WebService
parent0b4ee129cc3a16943a39f01494f8167791a8d38a (diff)
downloadbugs-6a10905af0af5b3436a344cf9181d29fcea4083a.tar
bugs-6a10905af0af5b3436a344cf9181d29fcea4083a.tar.gz
bugs-6a10905af0af5b3436a344cf9181d29fcea4083a.tar.bz2
bugs-6a10905af0af5b3436a344cf9181d29fcea4083a.tar.xz
bugs-6a10905af0af5b3436a344cf9181d29fcea4083a.zip
Bug 475151: Refactor the XML-RPC server stuff out of Bugzilla::WebService
Patch By Max Kanat-Alexander <mkanat@bugzilla.org> r=dkl, a=mkanat
Diffstat (limited to 'Bugzilla/WebService')
-rwxr-xr-xBugzilla/WebService/Constants.pm24
-rw-r--r--Bugzilla/WebService/Server.pm42
-rw-r--r--Bugzilla/WebService/Server/XMLRPC.pm170
3 files changed, 232 insertions, 4 deletions
diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm
index 2ccca5ae9..172d757ef 100755
--- a/Bugzilla/WebService/Constants.pm
+++ b/Bugzilla/WebService/Constants.pm
@@ -20,13 +20,13 @@ package Bugzilla::WebService::Constants;
use strict;
use base qw(Exporter);
-@Bugzilla::WebService::Constants::EXPORT = qw(
+our @EXPORT = qw(
WS_ERROR_CODE
ERROR_UNKNOWN_FATAL
ERROR_UNKNOWN_TRANSIENT
-
ERROR_AUTH_NODATA
- ERROR_UNIMPLEMENTED
+
+ WS_DISPATCH
);
# This maps the error names in global/*-error.html.tmpl to numbers.
@@ -115,7 +115,23 @@ use constant ERROR_UNKNOWN_FATAL => -32000;
use constant ERROR_UNKNOWN_TRANSIENT => 32000;
use constant ERROR_AUTH_NODATA => 410;
-use constant ERROR_UNIMPLEMENTED => 910;
use constant ERROR_GENERAL => 999;
+sub WS_DISPATCH {
+ # We "require" here instead of "use" above to avoid a dependency loop.
+ require Bugzilla::Hook;
+ my %hook_dispatch;
+ Bugzilla::Hook::process('webservice', { dispatch => \%hook_dispatch });
+
+ my $dispatch = {
+ 'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
+ 'Bug' => 'Bugzilla::WebService::Bug',
+ 'User' => 'Bugzilla::WebService::User',
+ 'Product' => 'Bugzilla::WebService::Product',
+ %hook_dispatch
+ };
+ return $dispatch;
+};
+
+
1;
diff --git a/Bugzilla/WebService/Server.pm b/Bugzilla/WebService/Server.pm
new file mode 100644
index 000000000..dfb9f559a
--- /dev/null
+++ b/Bugzilla/WebService/Server.pm
@@ -0,0 +1,42 @@
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Bugzilla Bug Tracking System.
+#
+# Contributor(s): Marc Schumann <wurblzap@gmail.com>
+# Max Kanat-Alexander <mkanat@bugzilla.org>
+
+package Bugzilla::WebService::Server;
+use strict;
+use Bugzilla::Util qw(ssl_require_redirect);
+
+sub handle_login {
+ my ($self, $class, $method, $full_method) = @_;
+ eval "require $class";
+ return if $class->login_exempt($method);
+ Bugzilla->login();
+
+ # Even though we check for the need to redirect in
+ # Bugzilla->login() we check here again since Bugzilla->login()
+ # does not know what the current XMLRPC method is. Therefore
+ # ssl_require_redirect in Bugzilla->login() will have returned
+ # false if system was configured to redirect for authenticated
+ # sessions and the user was not yet logged in.
+ # So here we pass in the method name to ssl_require_redirect so
+ # it can then check for the extra case where the method equals
+ # User.login, which we would then need to redirect if not
+ # over a secure connection.
+ Bugzilla->cgi->require_https(Bugzilla->params->{'sslbase'})
+ if ssl_require_redirect($full_method);
+}
+
+1;
diff --git a/Bugzilla/WebService/Server/XMLRPC.pm b/Bugzilla/WebService/Server/XMLRPC.pm
new file mode 100644
index 000000000..36b4e01fd
--- /dev/null
+++ b/Bugzilla/WebService/Server/XMLRPC.pm
@@ -0,0 +1,170 @@
+# -*- Mode: perl; indent-tabs-mode: nil -*-
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Bugzilla Bug Tracking System.
+#
+# Contributor(s): Marc Schumann <wurblzap@gmail.com>
+# Max Kanat-Alexander <mkanat@bugzilla.org>
+
+package Bugzilla::WebService::Server::XMLRPC;
+
+use strict;
+use XMLRPC::Transport::HTTP;
+use Bugzilla::WebService::Server;
+our @ISA = qw(XMLRPC::Transport::HTTP::CGI Bugzilla::WebService::Server);
+
+use Bugzilla::WebService::Constants;
+
+sub initialize {
+ my $self = shift;
+ my %retval = $self->SUPER::initialize(@_);
+ $retval{'serializer'} = Bugzilla::XMLRPC::Serializer->new;
+ $retval{'deserializer'} = Bugzilla::XMLRPC::Deserializer->new;
+ $retval{'dispatch_with'} = WS_DISPATCH;
+ return %retval;
+}
+
+sub make_response {
+ my $self = shift;
+
+ $self->SUPER::make_response(@_);
+
+ # XMLRPC::Transport::HTTP::CGI doesn't know about Bugzilla carrying around
+ # its cookies in Bugzilla::CGI, so we need to copy them over.
+ foreach (@{Bugzilla->cgi->{'Bugzilla_cookie_list'}}) {
+ $self->response->headers->push_header('Set-Cookie', $_);
+ }
+}
+
+sub datetime_format {
+ my ($self, $date_string) = @_;
+
+ my $time = str2time($date_string);
+ my ($sec, $min, $hour, $mday, $mon, $year) = localtime $time;
+ # This format string was stolen from SOAP::Utils->format_datetime,
+ # which doesn't work but which has almost the right format string.
+ my $iso_datetime = sprintf('%d%02d%02dT%02d:%02d:%02d',
+ $year + 1900, $mon + 1, $mday, $hour, $min, $sec);
+ return $iso_datetime;
+}
+
+sub handle_login {
+ my ($self, $classes, $action, $uri, $method) = @_;
+ my $class = $classes->{$uri};
+ my $full_method = $uri . "." . $method;
+ $self->SUPER::handle_login($class, $method, $full_method);
+ return;
+}
+
+1;
+
+# This exists to validate input parameters (which XMLRPC::Lite doesn't do)
+# and also, in some cases, to more-usefully decode them.
+package Bugzilla::XMLRPC::Deserializer;
+use strict;
+# We can't use "use base" because XMLRPC::Serializer doesn't return
+# a true value.
+eval { require XMLRPC::Lite; };
+our @ISA = qw(XMLRPC::Deserializer);
+
+use Bugzilla::Error;
+
+# Some method arguments need to be converted in some way, when they are input.
+sub decode_value {
+ my $self = shift;
+ my ($type) = @{ $_[0] };
+ my $value = $self->SUPER::decode_value(@_);
+
+ # We only validate/convert certain types here.
+ return $value if $type !~ /^(?:int|i4|boolean|double|dateTime\.iso8601)$/;
+
+ # Though the XML-RPC standard doesn't allow an empty <int>,
+ # <double>,or <dateTime.iso8601>, we do, and we just say
+ # "that's undef".
+ if (grep($type eq $_, qw(int double dateTime))) {
+ return undef if $value eq '';
+ }
+
+ my $validator = $self->_validation_subs->{$type};
+ if (!$validator->($value)) {
+ ThrowUserError('xmlrpc_invalid_value',
+ { type => $type, value => $value });
+ }
+
+ # We convert dateTimes to a DB-friendly date format.
+ if ($type eq 'dateTime.iso8601') {
+ # We leave off the $ from the end of this regex to allow for possible
+ # extensions to the XML-RPC date standard.
+ $value =~ /^(\d{4})(\d{2})(\d{2})T(\d{2}):(\d{2}):(\d{2})/;
+ $value = "$1-$2-$3 $4:$5:$6";
+ }
+
+ return $value;
+}
+
+sub _validation_subs {
+ my $self = shift;
+ return $self->{_validation_subs} if $self->{_validation_subs};
+ # The only place that XMLRPC::Lite stores any sort of validation
+ # regex is in XMLRPC::Serializer. We want to re-use those regexes here.
+ my $lookup = Bugzilla::XMLRPC::Serializer->new->typelookup;
+
+ # $lookup is a hash whose values are arrayrefs, and whose keys are the
+ # names of types. The second item of each arrayref is a subroutine
+ # that will do our validation for us.
+ my %validators = map { $_ => $lookup->{$_}->[1] } (keys %$lookup);
+ # Add a boolean validator
+ $validators{'boolean'} = sub {$_[0] =~ /^[01]$/};
+ # Some types have multiple names, or have a different name in
+ # XMLRPC::Serializer than their standard XML-RPC name.
+ $validators{'dateTime.iso8601'} = $validators{'dateTime'};
+ $validators{'i4'} = $validators{'int'};
+
+ $self->{_validation_subs} = \%validators;
+ return \%validators;
+}
+
+1;
+
+# This package exists to fix a UTF-8 bug in SOAP::Lite.
+# See http://rt.cpan.org/Public/Bug/Display.html?id=32952.
+package Bugzilla::XMLRPC::Serializer;
+use strict;
+# We can't use "use base" because XMLRPC::Serializer doesn't return
+# a true value.
+eval { require XMLRPC::Lite; };
+our @ISA = qw(XMLRPC::Serializer);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(@_);
+ # This fixes UTF-8.
+ $self->{'_typelookup'}->{'base64'} =
+ [10, sub { !utf8::is_utf8($_[0]) && $_[0] =~ /[^\x09\x0a\x0d\x20-\x7f]/},
+ 'as_base64'];
+ # This makes arrays work right even though we're a subclass.
+ # (See http://rt.cpan.org//Ticket/Display.html?id=34514)
+ $self->{'_encodingStyle'} = '';
+ return $self;
+}
+
+sub as_string {
+ my $self = shift;
+ my ($value) = @_;
+ # Something weird happens with XML::Parser when we have upper-ASCII
+ # characters encoded as UTF-8, and this fixes it.
+ utf8::encode($value) if utf8::is_utf8($value)
+ && $value =~ /^[\x00-\xff]+$/;
+ return $self->SUPER::as_string($value);
+}
+
+1;