Bug 10856: Improve the previous and next items on the shelf browser
[koha.git] / opac / ilsdi.pl
index b22bd69..d68f09a 100755 (executable)
 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 #
-# You should have received a copy of the GNU General Public License along with
-# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-# Suite 330, Boston, MA  02111-1307 USA
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use strict;
+use warnings;
+
+use List::MoreUtils qw(any);
 
 use C4::ILSDI::Services;
 use C4::Auth;
@@ -28,7 +33,7 @@ use CGI;
 
 This script is a basic implementation of ILS-DI protocol for Koha.
 It acts like a dispatcher, that get the CGI request, check required and 
-optionals arguments, call a function from C4::ILS-DI::Services, and finaly 
+optionals arguments, call a function from C4::ILS-DI, and finaly
 outputs the returned hashref as XML.
 
 =cut
@@ -38,182 +43,201 @@ my $cgi = new CGI;
 
 # List of available services, sorted by level
 my @services = (
-       'Describe',                          # Not part of ILS-DI, online API doc
-#      Level 1: Basic Discovery Interfaces
-#      'HarvestBibliographicRecords',       # OAI-PMH
-#      'HarvestExpandedRecords',            # OAI-PMH
-       'GetAvailability',                   # FIXME Add bibbliographic level
-#      'GoToBibliographicRequestPage'       # I don't understant this one
-#      Level 2: Elementary OPAC supplement
-#      'HarvestAuthorityRecords',           # OAI-PMH
-#      'HarvestHoldingsRecords',            # OAI-PMH
-       'GetRecords',                        # Note that we can use OAI-PMH for this too
-#      'Search',                            # TODO
-#      'Scan',                              # TODO
-       'GetAuthorityRecords',               
-#      'OutputRewritablePage',              # I don't understant this one
-#      'OutputIntermediateFormat',          # I don't understant this one
-#      Level 3: Elementary OPAC alternative
-       'LookupPatron',
-       'AuthenticatePatron',
-       'GetPatronInfo',
-       'GetPatronStatus',
-       'GetServices',                       # FIXME Loans
-       'RenewLoan', 
-       'HoldTitle',                         # FIXME Add dates support
-       'HoldItem',                          # FIXME Add dates support
-       'CancelHold',
-#      'RecallItem',                        # Not supported by Koha
-#      'CancelRecall',                      # Not supported by Koha
-#      Level 4: Robust/domain specific discovery platforms
-#      'SearchCourseReserves',              # TODO 
-#      'Explain'                            # TODO
+    'Describe',    # Not part of ILS-DI, online API doc
+
+    #  Level 1: Basic Discovery Interfaces
+    #  'HarvestBibliographicRecords',       # OAI-PMH
+    #  'HarvestExpandedRecords',            # OAI-PMH
+    'GetAvailability',    # FIXME Add bibbliographic level
+
+    #  'GoToBibliographicRequestPage'       # I don't understant this one
+    #  Level 2: Elementary OPAC supplement
+    #  'HarvestAuthorityRecords',           # OAI-PMH
+    #  'HarvestHoldingsRecords',            # OAI-PMH
+    'GetRecords',         # Note that we can use OAI-PMH for this too
+
+    #  'Search',                            # TODO
+    #  'Scan',                              # TODO
+    'GetAuthorityRecords',
+
+    #  'OutputRewritablePage',              # I don't understant this one
+    #  'OutputIntermediateFormat',          # I don't understant this one
+    #  Level 3: Elementary OPAC alternative
+    'LookupPatron',
+    'AuthenticatePatron',
+    'GetPatronInfo',
+    'GetPatronStatus',
+    'GetServices',    # FIXME Loans
+    'RenewLoan',
+    'HoldTitle',      # FIXME Add dates support
+    'HoldItem',       # FIXME Add dates support
+    'CancelHold',
+
+    #  'RecallItem',                        # Not supported by Koha
+    #  'CancelRecall',                      # Not supported by Koha
+    #  Level 4: Robust/domain specific discovery platforms
+    #  'SearchCourseReserves',              # TODO
+    #  'Explain'                            # TODO
 );
 
 # List of required arguments
 my %required = (
-       'Describe'            => ['verb'],
-       'GetAvailability'     => ['id', 'id_type'],
-       'GetRecords'          => ['id'],
-       'GetAuthorityRecords' => ['id'],
-       'LookupPatron'        => ['id'],
-       'AuthenticatePatron'  => ['username', 'password'],
-       'GetPatronInfo'       => ['patron_id'],
-       'GetPatronStatus'     => ['patron_id'],
-       'GetServices'         => ['patron_id', 'item_id'],
-       'RenewLoan'           => ['patron_id', 'item_id'],
-       'HoldTitle'           => ['patron_id', 'bib_id', 'request_location'],
-       'HoldItem'            => ['patron_id', 'bib_id', 'item_id'],
-       'CancelHold'          => ['patron_id', 'item_id'],
+    'Describe'            => ['verb'],
+    'GetAvailability'     => [ 'id', 'id_type' ],
+    'GetRecords'          => ['id'],
+    'GetAuthorityRecords' => ['id'],
+    'LookupPatron'        => ['id'],
+    'AuthenticatePatron'  => [ 'username', 'password' ],
+    'GetPatronInfo'       => ['patron_id'],
+    'GetPatronStatus'     => ['patron_id'],
+    'GetServices'         => [ 'patron_id', 'item_id' ],
+    'RenewLoan'           => [ 'patron_id', 'item_id' ],
+    'HoldTitle'           => [ 'patron_id', 'bib_id', 'request_location' ],
+    'HoldItem'            => [ 'patron_id', 'bib_id', 'item_id' ],
+    'CancelHold' => [ 'patron_id', 'item_id' ],
 );
 
 # List of optional arguments
 my %optional = (
-       'Describe'            => [],
-       'GetAvailability'     => ['return_type', 'return_fmt'],
-       'GetRecords'          => ['schema'],
-       'GetAuthorityRecords' => ['schema'],
-       'LookupPatron'        => ['id_type'],
-       'AuthenticatePatron'  => [],
-       'GetPatronInfo'       => ['show_contact', 'show_fines', 'show_holds', 'show_loans'],
-       'GetPatronStatus'     => [],
-       'GetServices'         => [],
-       'RenewLoan'           => ['desired_due_date'],
-       'HoldTitle'           => ['pickup_location', 'needed_before_date', 'pickup_expiry_date'],
-       'HoldItem'            => ['pickup_location', 'needed_before_date', 'pickup_expiry_date'],
-       'CancelHold'          => [],
+    'Describe'            => [],
+    'GetAvailability'     => [ 'return_type', 'return_fmt' ],
+    'GetRecords'          => ['schema'],
+    'GetAuthorityRecords' => ['schema'],
+    'LookupPatron'        => ['id_type'],
+    'AuthenticatePatron'  => [],
+    'GetPatronInfo'       => [ 'show_contact', 'show_fines', 'show_holds', 'show_loans' ],
+    'GetPatronStatus'     => [],
+    'GetServices'         => [],
+    'RenewLoan'           => ['desired_due_date'],
+    'HoldTitle'  => [ 'pickup_location', 'needed_before_date', 'pickup_expiry_date' ],
+    'HoldItem'   => [ 'pickup_location', 'needed_before_date', 'pickup_expiry_date' ],
+    'CancelHold' => [],
 );
 
-# If ILS-DI module is disabled in System->Preferences, redirect to 404
-if (not C4::Context->preference('ILS-DI')) {
-       print $cgi->redirect("/cgi-bin/koha/errors/404.pl");
-}
-
 # If no service is requested, display the online documentation
-if (not $cgi->param('service')) {
-       my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
-           {
-               template_name   => "ilsdi.tmpl",
-               query           => $cgi,
-               type            => "opac",
-               authnotrequired => 1,
-               debug           => 1,
-           }
-       );
-       output_html_with_http_headers $cgi, $cookie, $template->output;
-       exit 0;
+unless ( $cgi->param('service') ) {
+    my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+        {   template_name   => "ilsdi.tmpl",
+            query           => $cgi,
+            type            => "opac",
+            authnotrequired => 1,
+            debug           => 1,
+        }
+    );
+    output_html_with_http_headers $cgi, $cookie, $template->output;
+    exit 0;
 }
 
 # If user requested a service description, then display it
-if ($cgi->param('service') eq "Describe" and grep {$cgi->param('verb') eq $_} @services) {
-       my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
-           {
-               template_name   => "ilsdi.tmpl",
-               query           => $cgi,
-               type            => "opac",
-               authnotrequired => 1,
-               debug           => 1,
-           }
-       );
-       $template->param( $cgi->param('verb') => 1 );
-       output_html_with_http_headers $cgi, $cookie, $template->output;
-       exit 0;
+if ( $cgi->param('service') eq "Describe" and any { $cgi->param('verb') eq $_ } @services ) {
+    my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+        {   template_name   => "ilsdi.tmpl",
+            query           => $cgi,
+            type            => "opac",
+            authnotrequired => 1,
+            debug           => 1,
+        }
+    );
+    $template->param( $cgi->param('verb') => 1 );
+    output_html_with_http_headers $cgi, $cookie, $template->output;
+    exit 0;
 }
 
-my $service = $cgi->param('service') || "ilsdi";
+# any output after this point will be UTF-8 XML
+binmode STDOUT, ':encoding(UTF-8)';
+print CGI::header('-type'=>'text/xml', '-charset'=>'utf-8');
 
 my $out;
 
+# If ILS-DI module is disabled in System->Preferences, redirect to 404
+unless ( C4::Context->preference('ILS-DI') ) {
+    $out->{'code'} = "NotAllowed";
+    $out->{'message'} = "ILS-DI is disabled.";
+}
+
+# If the remote address is not allowed, redirect to 403
+my @AuthorizedIPs = split(/,/, C4::Context->preference('ILS-DI:AuthorizedIPs'));
+if ( @AuthorizedIPs # If no filter set, allow access to everybody
+    and not any { $ENV{'REMOTE_ADDR'} eq $_ } @AuthorizedIPs # IP Check
+    ) {
+    $out->{'code'} = "NotAllowed";
+    $out->{'message'} = "Unauthorized IP address: ".$ENV{'REMOTE_ADDR'}.".";
+}
+
+my $service = $cgi->param('service') || "ilsdi";
+
 # Check if the requested service is in the list
-if ($service and grep {$service eq $_} @services) {
-
-       my @parmsrequired = @{$required{$service}};
-       my @parmsoptional = @{$optional{$service}};
-       my @parmsall = (@parmsrequired, @parmsoptional);
-       my @names = $cgi->param;
-       my %paramhash = ();
-       foreach my $name (@names) {
-               $paramhash{$name} = 1;
-       }
-
-       # check for missing parameters
-       foreach my $name (@parmsrequired) {
-               if ((! exists $paramhash{$name})) {
-                       $out->{'message'} = "missing $name parameter";
-               }
-       }
-       
-       # check for illegal parameters
-       foreach my $name (@names) {
-               my $found = 0;
-               foreach my $name2 (@parmsall) {
-                       if ($name eq $name2) { 
-                               $found = 1; 
-                       }
-               }
-               if (($found == 0) && ($name ne 'service')) {
-                       $out->{'message'} = "$name is an illegal parameter";
-               }
-       }
-       
-       # check for multiple parameters
-       foreach my $name (@names) {
-               my @values = $cgi->param($name);
-               if ($#values != 0) {
-                       $out->{'message'} = "multiple values are not allowed for the $name parameter";
-               }
-       }
-
-       if (! $out->{'message'}) {
-               # GetAvailability is a special case, as it cannot use XML::Simple
-               if ($service eq "GetAvailability") {
-                       print CGI::header('text/xml');
-                       print C4::ILSDI::Services::GetAvailability($cgi);
-                       exit 0;
-               }
-               else {
-                       # Variable functions
-                       my $sub = do{
-                               no strict 'refs';
-                               my $symbol = 'C4::ILSDI::Services::'.$service;
-                               \&{"$symbol"};
-                       };
-                       # Call the requested service, and get its return value
-                       $out =  &$sub($cgi);
-               }
-       }
-} 
-else {
-       $out->{'message'} = "NotSupported";
+if ( $service and any { $service eq $_ } @services ) {
+
+    my @parmsrequired = @{ $required{$service} };
+    my @parmsoptional = @{ $optional{$service} };
+    my @parmsall      = ( @parmsrequired, @parmsoptional );
+    my @names         = $cgi->param;
+    my %paramhash;
+    $paramhash{$_} = 1 for @names;
+
+    # check for missing parameters
+    for ( @parmsrequired ) {
+        unless ( exists $paramhash{$_} ) {
+            $out->{'code'} = "MissingParameter";
+            $out->{'message'} = "The required parameter ".$_." is missing.";
+        }
+    }
+
+    # check for illegal parameters
+    for my $name ( @names ) {
+        my $found = 0;
+        for my $name2 (@parmsall) {
+            if ( $name eq $name2 ) {
+                $found = 1;
+            }
+        }
+        if ( $found == 0 && $name ne 'service' ) {
+            $out->{'code'} = "IllegalParameter";
+            $out->{'message'} = "The parameter ".$name." is illegal.";
+        }
+    }
+
+    # check for multiple parameters
+    for ( @names ) {
+        my @values = $cgi->param($_);
+        if ( $#values != 0 ) {
+            $out->{'code'} = "MultipleValuesNotAllowed";
+            $out->{'message'} = "Multiple values not allowed for the parameter ".$_.".";
+        }
+    }
+
+    if ( !$out->{'message'} ) {
+
+        # GetAvailability is a special case, as it cannot use XML::Simple
+        if ( $service eq "GetAvailability" ) {
+            print C4::ILSDI::Services::GetAvailability($cgi);
+            exit 0;
+        } else {
+
+            # Variable functions
+            my $sub = do {
+                no strict 'refs';
+                my $symbol = 'C4::ILSDI::Services::' . $service;
+                \&{"$symbol"};
+            };
+
+            # Call the requested service, and get its return value
+            $out = &$sub($cgi);
+        }
+    }
+} else {
+    $out->{'message'} = "NotSupported";
 }
 
 # Output XML by passing the hashref to XMLOut
-print CGI::header('text/xml');
-print XMLout($out,
-       noattr => 1, 
-       noescape => 1,
-       nosort => 1,
-       xmldecl => '<?xml version="1.0" encoding="ISO-8859-1" ?>', 
-       RootName => $service, 
-       SuppressEmpty => 1);
+print XMLout(
+    $out,
+    noattr        => 1,
+    nosort        => 1,
+    xmldecl       => '<?xml version="1.0" encoding="UTF-8" ?>',
+    RootName      => $service,
+    SuppressEmpty => 1
+);
+exit 0;