| File | C4/Auth.pm | Statements Executed | 78 | Total Time | 0.000783 seconds |
| Calls | Inclusive Time | Subroutine | |
|---|---|---|---|
| 1 | 0.05004 | C4::Auth:: | get_template_and_user |
| 1 | 0.01454 | C4::Auth:: | checkauth |
| 1 | 0.00670 | C4::Auth:: | get_session |
| 1 | 0.00147 | C4::Auth:: | _version_check |
| 0 | 0 | C4::Auth:: | BEGIN |
| 0 | 0 | C4::Auth:: | END |
| 0 | 0 | C4::Auth:: | _session_log |
| 0 | 0 | C4::Auth:: | check_api_auth |
| 0 | 0 | C4::Auth:: | check_cookie_auth |
| 0 | 0 | C4::Auth:: | checkpw |
| 0 | 0 | C4::Auth:: | get_all_subpermissions |
| 0 | 0 | C4::Auth:: | get_user_subpermissions |
| 0 | 0 | C4::Auth:: | getborrowernumber |
| 0 | 0 | C4::Auth:: | getuserflags |
| 0 | 0 | C4::Auth:: | haspermission |
| Line | Stmts. | Exclusive Time | Avg. | Code |
|---|---|---|---|---|
| 1 | ||||
| 2 | # -*- tab-width: 8 -*- | |||
| 3 | # NOTE: This file uses 8-character tabs; do not change the tab size! | |||
| 4 | ||||
| 5 | package C4::Auth; | |||
| 6 | ||||
| 7 | # Copyright 2000-2002 Katipo Communications | |||
| 8 | # | |||
| 9 | # This file is part of Koha. | |||
| 10 | # | |||
| 11 | # Koha is free software; you can redistribute it and/or modify it under the | |||
| 12 | # terms of the GNU General Public License as published by the Free Software | |||
| 13 | # Foundation; either version 2 of the License, or (at your option) any later | |||
| 14 | # version. | |||
| 15 | # | |||
| 16 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | |||
| 17 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |||
| 18 | # A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |||
| 19 | # | |||
| 20 | # You should have received a copy of the GNU General Public License along with | |||
| 21 | # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place, | |||
| 22 | # Suite 330, Boston, MA 02111-1307 USA | |||
| 23 | ||||
| 24 | use strict; | |||
| 25 | use Digest::MD5 qw(md5_base64); | |||
| 26 | use CGI::Session; | |||
| 27 | ||||
| 28 | require Exporter; | |||
| 29 | use C4::Context; | |||
| 30 | use C4::Output; # to get the template | |||
| 31 | use C4::Members; | |||
| 32 | use C4::Koha; | |||
| 33 | use C4::Branch; # GetBranches | |||
| 34 | use C4::VirtualShelves 3.02 qw(GetShelvesSummary); | |||
| 35 | ||||
| 36 | # use utf8; | |||
| 37 | use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap); | |||
| 38 | ||||
| 39 | BEGIN { | |||
| 40 | $VERSION = 3.02; # set version for version checking | |||
| 41 | $debug = $ENV{DEBUG} || 0 ; | |||
| 42 | @ISA = qw(Exporter); | |||
| 43 | @EXPORT = qw(&checkauth &get_template_and_user); | |||
| 44 | @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); | |||
| 45 | %EXPORT_TAGS = (EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)]); | |||
| 46 | $ldap = C4::Context->config('useldapserver') || 0; | |||
| 47 | if ($ldap) { | |||
| 48 | require C4::Auth_with_ldap; # no import | |||
| 49 | import C4::Auth_with_ldap qw(checkpw_ldap); | |||
| 50 | } | |||
| 51 | } | |||
| 52 | ||||
| 53 | =head1 NAME | |||
| 54 | ||||
| 55 | C4::Auth - Authenticates Koha users | |||
| 56 | ||||
| 57 | =head1 SYNOPSIS | |||
| 58 | ||||
| 59 | use CGI; | |||
| 60 | use C4::Auth; | |||
| 61 | use C4::Output; | |||
| 62 | ||||
| 63 | my $query = new CGI; | |||
| 64 | ||||
| 65 | my ($template, $borrowernumber, $cookie) | |||
| 66 | = get_template_and_user( | |||
| 67 | { | |||
| 68 | template_name => "opac-main.tmpl", | |||
| 69 | query => $query, | |||
| 70 | type => "opac", | |||
| 71 | authnotrequired => 1, | |||
| 72 | flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' }, | |||
| 73 | } | |||
| 74 | ); | |||
| 75 | ||||
| 76 | output_html_with_http_headers $query, $cookie, $template->output; | |||
| 77 | ||||
| 78 | =head1 DESCRIPTION | |||
| 79 | ||||
| 80 | The main function of this module is to provide | |||
| 81 | authentification. However the get_template_and_user function has | |||
| 82 | been provided so that a users login information is passed along | |||
| 83 | automatically. This gets loaded into the template. | |||
| 84 | ||||
| 85 | =head1 FUNCTIONS | |||
| 86 | ||||
| 87 | =over 2 | |||
| 88 | ||||
| 89 | =item get_template_and_user | |||
| 90 | ||||
| 91 | my ($template, $borrowernumber, $cookie) | |||
| 92 | = get_template_and_user( | |||
| 93 | { | |||
| 94 | template_name => "opac-main.tmpl", | |||
| 95 | query => $query, | |||
| 96 | type => "opac", | |||
| 97 | authnotrequired => 1, | |||
| 98 | flagsrequired => {borrow => 1, catalogue => '*', tools => 'import_patrons' }, | |||
| 99 | } | |||
| 100 | ); | |||
| 101 | ||||
| 102 | This call passes the C<query>, C<flagsrequired> and C<authnotrequired> | |||
| 103 | to C<&checkauth> (in this module) to perform authentification. | |||
| 104 | See C<&checkauth> for an explanation of these parameters. | |||
| 105 | ||||
| 106 | The C<template_name> is then used to find the correct template for | |||
| 107 | the page. The authenticated users details are loaded onto the | |||
| 108 | template in the HTML::Template LOOP variable C<USER_INFO>. Also the | |||
| 109 | C<sessionID> is passed to the template. This can be used in templates | |||
| 110 | if cookies are disabled. It needs to be put as and input to every | |||
| 111 | authenticated page. | |||
| 112 | ||||
| 113 | More information on the C<gettemplate> sub can be found in the | |||
| 114 | Output.pm module. | |||
| 115 | ||||
| 116 | =cut | |||
| 117 | ||||
| 118 | # spent 0.05004s within C4::Auth::get_template_and_user which was called:
# 1 times (0.05004s) at line 37 of opac/opac-main.pl sub get_template_and_user { | |||
| 119 | 10 | 0.00015 | 0.00001 | my $in = shift; |
| 120 | my $template = # spent 0.02097s making 1 calls to C4::Output::gettemplate | |||
| 121 | gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} ); | |||
| 122 | my ( $user, $cookie, $sessionID, $flags ) = checkauth( # spent 0.01454s making 1 calls to C4::Auth::checkauth | |||
| 123 | $in->{'query'}, | |||
| 124 | $in->{'authnotrequired'}, | |||
| 125 | $in->{'flagsrequired'}, | |||
| 126 | $in->{'type'} | |||
| 127 | ) unless ($in->{'template_name'}=~/maintenance/); | |||
| 128 | ||||
| 129 | my $borrowernumber; | |||
| 130 | my $insecure = C4::Context->preference('insecure'); # spent 0.00028s making 1 calls to C4::Context::preference | |||
| 131 | 11 | 0.00007 | 7e-06 | if ($user or $insecure) { |
| 132 | ||||
| 133 | # load the template variables for stylesheets and JavaScript | |||
| 134 | $template->param( css_libs => $in->{'css_libs'} ); | |||
| 135 | $template->param( css_module => $in->{'css_module'} ); | |||
| 136 | $template->param( css_page => $in->{'css_page'} ); | |||
| 137 | $template->param( css_widgets => $in->{'css_widgets'} ); | |||
| 138 | ||||
| 139 | $template->param( js_libs => $in->{'js_libs'} ); | |||
| 140 | $template->param( js_module => $in->{'js_module'} ); | |||
| 141 | $template->param( js_page => $in->{'js_page'} ); | |||
| 142 | $template->param( js_widgets => $in->{'js_widgets'} ); | |||
| 143 | ||||
| 144 | # user info | |||
| 145 | $template->param( loggedinusername => $user ); | |||
| 146 | $template->param( sessionID => $sessionID ); | |||
| 147 | ||||
| 148 | my ($pubshelves, $barshelves) = C4::Context->get_shelves_userenv(); | |||
| 149 | if (defined($pubshelves)) { | |||
| 150 | $template->param( pubshelves => scalar (@$pubshelves)); | |||
| 151 | $template->param( pubshelvesloop => $pubshelves); | |||
| 152 | } | |||
| 153 | if (defined($barshelves)) { | |||
| 154 | $template->param( barshelves => scalar (@$barshelves)); | |||
| 155 | $template->param( barshelvesloop => $barshelves); | |||
| 156 | } | |||
| 157 | ||||
| 158 | $borrowernumber = getborrowernumber($user); | |||
| 159 | my ( $borr ) = GetMemberDetails( $borrowernumber ); | |||
| 160 | my @bordat; | |||
| 161 | $bordat[0] = $borr; | |||
| 162 | $template->param( "USER_INFO" => \@bordat ); | |||
| 163 | ||||
| 164 | my $all_perms = get_all_subpermissions(); | |||
| 165 | ||||
| 166 | my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow | |||
| 167 | editcatalogue updatecharges management tools editauthorities serials reports); | |||
| 168 | # We are going to use the $flags returned by checkauth | |||
| 169 | # to create the template's parameters that will indicate | |||
| 170 | # which menus the user can access. | |||
| 171 | if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) { | |||
| 172 | $template->param( CAN_user_circulate => 1 ); | |||
| 173 | $template->param( CAN_user_catalogue => 1 ); | |||
| 174 | $template->param( CAN_user_parameters => 1 ); | |||
| 175 | $template->param( CAN_user_borrowers => 1 ); | |||
| 176 | $template->param( CAN_user_permissions => 1 ); | |||
| 177 | $template->param( CAN_user_reserveforothers => 1 ); | |||
| 178 | $template->param( CAN_user_borrow => 1 ); | |||
| 179 | $template->param( CAN_user_editcatalogue => 1 ); | |||
| 180 | $template->param( CAN_user_updatecharges => 1 ); | |||
| 181 | $template->param( CAN_user_acquisition => 1 ); | |||
| 182 | $template->param( CAN_user_management => 1 ); | |||
| 183 | $template->param( CAN_user_tools => 1 ); | |||
| 184 | $template->param( CAN_user_editauthorities => 1 ); | |||
| 185 | $template->param( CAN_user_serials => 1 ); | |||
| 186 | $template->param( CAN_user_reports => 1 ); | |||
| 187 | $template->param( CAN_user_staffaccess => 1 ); | |||
| 188 | foreach my $module (keys %$all_perms) { | |||
| 189 | foreach my $subperm (keys %{ $all_perms->{$module} }) { | |||
| 190 | $template->param( "CAN_user_${module}_${subperm}" => 1 ); | |||
| 191 | } | |||
| 192 | } | |||
| 193 | } | |||
| 194 | ||||
| 195 | if (C4::Context->preference('GranularPermissions')) { | |||
| 196 | if ( $flags ) { | |||
| 197 | foreach my $module (keys %$all_perms) { | |||
| 198 | if ( $flags->{$module} == 1) { | |||
| 199 | foreach my $subperm (keys %{ $all_perms->{$module} }) { | |||
| 200 | $template->param( "CAN_user_${module}_${subperm}" => 1 ); | |||
| 201 | } | |||
| 202 | } elsif ( ref($flags->{$module}) ) { | |||
| 203 | foreach my $subperm (keys %{ $flags->{$module} } ) { | |||
| 204 | $template->param( "CAN_user_${module}_${subperm}" => 1 ); | |||
| 205 | } | |||
| 206 | } | |||
| 207 | } | |||
| 208 | } | |||
| 209 | } else { | |||
| 210 | foreach my $module (keys %$all_perms) { | |||
| 211 | foreach my $subperm (keys %{ $all_perms->{$module} }) { | |||
| 212 | $template->param( "CAN_user_${module}_${subperm}" => 1 ); | |||
| 213 | } | |||
| 214 | } | |||
| 215 | } | |||
| 216 | ||||
| 217 | if ($flags) { | |||
| 218 | foreach my $module (keys %$flags) { | |||
| 219 | if ( $flags->{$module} == 1 or ref($flags->{$module}) ) { | |||
| 220 | $template->param( "CAN_user_$module" => 1 ); | |||
| 221 | if ($module eq "parameters") { | |||
| 222 | $template->param( CAN_user_management => 1 ); | |||
| 223 | } | |||
| 224 | } | |||
| 225 | } | |||
| 226 | } | |||
| 227 | } | |||
| 228 | else { # if this is an anonymous session, setup to display public lists... | |||
| 229 | ||||
| 230 | # load the template variables for stylesheets and JavaScript | |||
| 231 | $template->param( css_libs => $in->{'css_libs'} ); # spent 0.00003s making 1 calls to HTML::Template::Pro::param | |||
| 232 | $template->param( css_module => $in->{'css_module'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 233 | $template->param( css_page => $in->{'css_page'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 234 | $template->param( css_widgets => $in->{'css_widgets'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 235 | ||||
| 236 | $template->param( js_libs => $in->{'js_libs'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 237 | $template->param( js_module => $in->{'js_module'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 238 | $template->param( js_page => $in->{'js_page'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 239 | $template->param( js_widgets => $in->{'js_widgets'} ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 240 | ||||
| 241 | $template->param( sessionID => $sessionID ); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 242 | ||||
| 243 | my ($pubshelves) = C4::Context->get_shelves_userenv(); # an anonymous user has no 'barshelves'... # spent 0.00002s making 1 calls to C4::Context::get_shelves_userenv | |||
| 244 | 2 | 0.00001 | 7e-06 | if (defined(($pubshelves))) { |
| 245 | $template->param( pubshelves => scalar (@$pubshelves)); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 246 | $template->param( pubshelvesloop => $pubshelves); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 247 | } | |||
| 248 | ||||
| 249 | } | |||
| 250 | ||||
| 251 | # these template parameters are set the same regardless of $in->{'type'} | |||
| 252 | $template->param( # spent 0.00247s making 9 calls to C4::Context::preference, avg 0.00027s/call
# spent 0.00009s making 1 calls to HTML::Template::Pro::param
# spent 0.00004s making 6 calls to C4::Context::userenv, avg 7e-06s/call | |||
| 253 | "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1, | |||
| 254 | EnhancedMessagingPreferences => C4::Context->preference('EnhancedMessagingPreferences'), | |||
| 255 | GoogleJackets => C4::Context->preference("GoogleJackets"), | |||
| 256 | KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"), | |||
| 257 | LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"), | |||
| 258 | LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"), | |||
| 259 | LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", | |||
| 260 | TagsEnabled => C4::Context->preference("TagsEnabled"), | |||
| 261 | hide_marc => C4::Context->preference("hide_marc"), | |||
| 262 | 'item-level_itypes' => C4::Context->preference('item-level_itypes'), | |||
| 263 | patronimages => C4::Context->preference("patronimages"), | |||
| 264 | singleBranchMode => C4::Context->preference("singleBranchMode"), | |||
| 265 | ); | |||
| 266 | ||||
| 267 | 5 | 0.00025 | 0.00005 | if ( $in->{'type'} eq "intranet" ) { |
| 268 | $template->param( | |||
| 269 | AmazonContent => C4::Context->preference("AmazonContent"), | |||
| 270 | AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"), | |||
| 271 | AutoLocation => C4::Context->preference("AutoLocation"), | |||
| 272 | "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1, | |||
| 273 | CircAutocompl => C4::Context->preference("CircAutocompl"), | |||
| 274 | FRBRizeEditions => C4::Context->preference("FRBRizeEditions"), | |||
| 275 | IndependantBranches => C4::Context->preference("IndependantBranches"), | |||
| 276 | IntranetNav => C4::Context->preference("IntranetNav"), | |||
| 277 | IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"), | |||
| 278 | LibraryName => C4::Context->preference("LibraryName"), | |||
| 279 | LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"), | |||
| 280 | TemplateEncoding => C4::Context->preference("TemplateEncoding"), | |||
| 281 | advancedMARCEditor => C4::Context->preference("advancedMARCEditor"), | |||
| 282 | canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'), | |||
| 283 | intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), | |||
| 284 | intranetreadinghistory => C4::Context->preference("intranetreadinghistory"), | |||
| 285 | intranetstylesheet => C4::Context->preference("intranetstylesheet"), | |||
| 286 | intranetuserjs => C4::Context->preference("intranetuserjs"), | |||
| 287 | noItemTypeImages => C4::Context->preference("noItemTypeImages"), | |||
| 288 | suggestion => C4::Context->preference("suggestion"), | |||
| 289 | virtualshelves => C4::Context->preference("virtualshelves"), | |||
| 290 | ); | |||
| 291 | } | |||
| 292 | else { | |||
| 293 | warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' ); | |||
| 294 | my $LibraryNameTitle = C4::Context->preference("LibraryName"); # spent 0.00027s making 1 calls to C4::Context::preference | |||
| 295 | $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi; | |||
| 296 | $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg; | |||
| 297 | $template->param( # spent 0.01029s making 38 calls to C4::Context::preference, avg 0.00027s/call
# spent 0.00026s making 1 calls to CGI::AUTOLOAD
# spent 0.00022s making 1 calls to HTML::Template::Pro::param
# spent 0.00002s making 1 calls to CGI::https
# spent 0.00002s making 2 calls to C4::Context::userenv, avg 8e-06s/call | |||
| 298 | AmazonContent => "" . C4::Context->preference("AmazonContent"), | |||
| 299 | AnonSuggestions => "" . C4::Context->preference("AnonSuggestions"), | |||
| 300 | AuthorisedValueImages => C4::Context->preference("AuthorisedValueImages"), | |||
| 301 | LibraryName => "" . C4::Context->preference("LibraryName"), | |||
| 302 | LibraryNameTitle => "" . $LibraryNameTitle, | |||
| 303 | LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", | |||
| 304 | OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"), | |||
| 305 | OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"), | |||
| 306 | OPACItemHolds => C4::Context->preference("OPACItemHolds"), | |||
| 307 | OPACShelfBrowser => "". C4::Context->preference("OPACShelfBrowser"), | |||
| 308 | OPACURLOpenInNewWindow => "" . C4::Context->preference("OPACURLOpenInNewWindow"), | |||
| 309 | OPACUserCSS => "". C4::Context->preference("OPACUserCSS"), | |||
| 310 | OPACViewOthersSuggestions => "" . C4::Context->preference("OPACViewOthersSuggestions"), | |||
| 311 | OpacAuthorities => C4::Context->preference("OpacAuthorities"), | |||
| 312 | OPACBaseURL => ($in->{'query'}->https() ? "https://" : "http://") . | |||
| 313 | $ENV{'SERVER_NAME'} . | |||
| 314 | ($ENV{'SERVER_PORT'} eq ($in->{'query'}->https() ? "443" : "80") ? '' : ":$ENV{'SERVER_PORT'}"), | |||
| 315 | OpacBrowser => C4::Context->preference("OpacBrowser"), | |||
| 316 | OpacCloud => C4::Context->preference("OpacCloud"), | |||
| 317 | OpacMainUserBlock => "" . C4::Context->preference("OpacMainUserBlock"), | |||
| 318 | OpacNav => "" . C4::Context->preference("OpacNav"), | |||
| 319 | OpacPasswordChange => C4::Context->preference("OpacPasswordChange"), | |||
| 320 | OpacTopissue => C4::Context->preference("OpacTopissue"), | |||
| 321 | RequestOnOpac => C4::Context->preference("RequestOnOpac"), | |||
| 322 | TemplateEncoding => "". C4::Context->preference("TemplateEncoding"), | |||
| 323 | 'Version' => C4::Context->preference('Version'), | |||
| 324 | XSLTDetailsDisplay => C4::Context->preference("XSLTDetailsDisplay"), | |||
| 325 | XSLTResultsDisplay => C4::Context->preference("XSLTResultsDisplay"), | |||
| 326 | hidelostitems => C4::Context->preference("hidelostitems"), | |||
| 327 | mylibraryfirst => C4::Context->preference("SearchMyLibraryFirst"), | |||
| 328 | opacbookbag => "" . C4::Context->preference("opacbookbag"), | |||
| 329 | opaccolorstylesheet => "". C4::Context->preference("opaccolorstylesheet"), | |||
| 330 | opaccredits => "" . C4::Context->preference("opaccredits"), | |||
| 331 | opacheader => "" . C4::Context->preference("opacheader"), | |||
| 332 | opaclanguagesdisplay => "". C4::Context->preference("opaclanguagesdisplay"), | |||
| 333 | opaclayoutstylesheet => "". C4::Context->preference("opaclayoutstylesheet"), | |||
| 334 | opacreadinghistory => C4::Context->preference("opacreadinghistory"), | |||
| 335 | opacsmallimage => "" . C4::Context->preference("opacsmallimage"), | |||
| 336 | opacuserjs => C4::Context->preference("opacuserjs"), | |||
| 337 | opacuserlogin => "" . C4::Context->preference("opacuserlogin"), | |||
| 338 | reviewson => C4::Context->preference("reviewson"), | |||
| 339 | suggestion => "" . C4::Context->preference("suggestion"), | |||
| 340 | virtualshelves => "" . C4::Context->preference("virtualshelves"), | |||
| 341 | ); | |||
| 342 | } | |||
| 343 | $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]); # spent 0.00002s making 1 calls to HTML::Template::Pro::param | |||
| 344 | return ( $template, $borrowernumber, $cookie, $flags); | |||
| 345 | } | |||
| 346 | ||||
| 347 | =item checkauth | |||
| 348 | ||||
| 349 | ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type); | |||
| 350 | ||||
| 351 | Verifies that the user is authorized to run this script. If | |||
| 352 | the user is authorized, a (userid, cookie, session-id, flags) | |||
| 353 | quadruple is returned. If the user is not authorized but does | |||
| 354 | not have the required privilege (see $flagsrequired below), it | |||
| 355 | displays an error page and exits. Otherwise, it displays the | |||
| 356 | login page and exits. | |||
| 357 | ||||
| 358 | Note that C<&checkauth> will return if and only if the user | |||
| 359 | is authorized, so it should be called early on, before any | |||
| 360 | unfinished operations (e.g., if you've opened a file, then | |||
| 361 | C<&checkauth> won't close it for you). | |||
| 362 | ||||
| 363 | C<$query> is the CGI object for the script calling C<&checkauth>. | |||
| 364 | ||||
| 365 | The C<$noauth> argument is optional. If it is set, then no | |||
| 366 | authorization is required for the script. | |||
| 367 | ||||
| 368 | C<&checkauth> fetches user and session information from C<$query> and | |||
| 369 | ensures that the user is authorized to run scripts that require | |||
| 370 | authorization. | |||
| 371 | ||||
| 372 | The C<$flagsrequired> argument specifies the required privileges | |||
| 373 | the user must have if the username and password are correct. | |||
| 374 | It should be specified as a reference-to-hash; keys in the hash | |||
| 375 | should be the "flags" for the user, as specified in the Members | |||
| 376 | intranet module. Any key specified must correspond to a "flag" | |||
| 377 | in the userflags table. E.g., { circulate => 1 } would specify | |||
| 378 | that the user must have the "circulate" privilege in order to | |||
| 379 | proceed. To make sure that access control is correct, the | |||
| 380 | C<$flagsrequired> parameter must be specified correctly. | |||
| 381 | ||||
| 382 | If the GranularPermissions system preference is ON, the | |||
| 383 | value of each key in the C<flagsrequired> hash takes on an additional | |||
| 384 | meaning, e.g., | |||
| 385 | ||||
| 386 | =item 1 | |||
| 387 | ||||
| 388 | The user must have access to all subfunctions of the module | |||
| 389 | specified by the hash key. | |||
| 390 | ||||
| 391 | =item * | |||
| 392 | ||||
| 393 | The user must have access to at least one subfunction of the module | |||
| 394 | specified by the hash key. | |||
| 395 | ||||
| 396 | =item specific permission, e.g., 'export_catalog' | |||
| 397 | ||||
| 398 | The user must have access to the specific subfunction list, which | |||
| 399 | must correspond to a row in the permissions table. | |||
| 400 | ||||
| 401 | The C<$type> argument specifies whether the template should be | |||
| 402 | retrieved from the opac or intranet directory tree. "opac" is | |||
| 403 | assumed if it is not specified; however, if C<$type> is specified, | |||
| 404 | "intranet" is assumed if it is not "opac". | |||
| 405 | ||||
| 406 | If C<$query> does not have a valid session ID associated with it | |||
| 407 | (i.e., the user has not logged in) or if the session has expired, | |||
| 408 | C<&checkauth> presents the user with a login page (from the point of | |||
| 409 | view of the original script, C<&checkauth> does not return). Once the | |||
| 410 | user has authenticated, C<&checkauth> restarts the original script | |||
| 411 | (this time, C<&checkauth> returns). | |||
| 412 | ||||
| 413 | The login page is provided using a HTML::Template, which is set in the | |||
| 414 | systempreferences table or at the top of this file. The variable C<$type> | |||
| 415 | selects which template to use, either the opac or the intranet | |||
| 416 | authentification template. | |||
| 417 | ||||
| 418 | C<&checkauth> returns a user ID, a cookie, and a session ID. The | |||
| 419 | cookie should be sent back to the browser; it verifies that the user | |||
| 420 | has authenticated. | |||
| 421 | ||||
| 422 | =cut | |||
| 423 | ||||
| 424 | # spent 0.00147s within C4::Auth::_version_check which was called:
# 1 times (0.00147s) by C4::Auth::checkauth at line 492 of C4/Auth.pm sub _version_check ($$) { | |||
| 425 | 9 | 0.00005 | 5e-06 | my $type = shift; |
| 426 | my $query = shift; | |||
| 427 | my $version; | |||
| 428 | # If Version syspref is unavailable, it means Koha is beeing installed, | |||
| 429 | # and so we must redirect to OPAC maintenance page or to the WebInstaller | |||
| 430 | # also, if OpacMaintenance is ON, OPAC should redirect to maintenance | |||
| 431 | if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') { # spent 0.00081s making 1 calls to C4::Context::preference | |||
| 432 | warn "OPAC Install required, redirecting to maintenance"; | |||
| 433 | print $query->redirect("/cgi-bin/koha/maintenance.pl"); | |||
| 434 | } | |||
| 435 | unless ($version = C4::Context->preference('Version')) { # assignment, not comparison # spent 0.00029s making 1 calls to C4::Context::preference | |||
| 436 | if ($type ne 'opac') { | |||
| 437 | warn "Install required, redirecting to Installer"; | |||
| 438 | print $query->redirect("/cgi-bin/koha/installer/install.pl"); | |||
| 439 | } | |||
| 440 | else { | |||
| 441 | warn "OPAC Install required, redirecting to maintenance"; | |||
| 442 | print $query->redirect("/cgi-bin/koha/maintenance.pl"); | |||
| 443 | } | |||
| 444 | exit; | |||
| 445 | } | |||
| 446 | ||||
| 447 | # check that database and koha version are the same | |||
| 448 | # there is no DB version, it's a fresh install, | |||
| 449 | # go to web installer | |||
| 450 | # there is a DB version, compare it to the code version | |||
| 451 | my $kohaversion=C4::Context::KOHAVERSION; # spent 0.00032s making 1 calls to C4::Context::KOHAVERSION | |||
| 452 | # remove the 3 last . to have a Perl number | |||
| 453 | $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; | |||
| 454 | $debug and print STDERR "kohaversion : $kohaversion\n"; | |||
| 455 | if ($version < $kohaversion){ | |||
| 456 | my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion"; | |||
| 457 | if ($type ne 'opac'){ | |||
| 458 | warn sprintf($warning, 'Installer'); | |||
| 459 | print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3"); | |||
| 460 | } else { | |||
| 461 | warn sprintf("OPAC: " . $warning, 'maintenance'); | |||
| 462 | print $query->redirect("/cgi-bin/koha/maintenance.pl"); | |||
| 463 | } | |||
| 464 | exit; | |||
| 465 | } | |||
| 466 | } | |||
| 467 | ||||
| 468 | sub _session_log { | |||
| 469 | (@_) or return 0; | |||
| 470 | open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog"; | |||
| 471 | printf L join("\n",@_); | |||
| 472 | close L; | |||
| 473 | } | |||
| 474 | ||||
| 475 | # spent 0.01454s within C4::Auth::checkauth which was called:
# 1 times (0.01454s) by C4::Auth::get_template_and_user at line 122 of C4/Auth.pm sub checkauth { | |||
| 476 | 19 | 0.00008 | 4e-06 | my $query = shift; |
| 477 | $debug and warn "Checking Auth"; | |||
| 478 | # $authnotrequired will be set for scripts which will run without authentication | |||
| 479 | my $authnotrequired = shift; | |||
| 480 | my $flagsrequired = shift; | |||
| 481 | my $type = shift; | |||
| 482 | $type = 'opac' unless $type; | |||
| 483 | ||||
| 484 | my $dbh = C4::Context->dbh; # spent 0.00026s making 1 calls to C4::Context::dbh | |||
| 485 | my $timeout = C4::Context->preference('timeout'); # spent 0.00030s making 1 calls to C4::Context::preference | |||
| 486 | # days | |||
| 487 | if ($timeout =~ /(\d+)[dD]/) { | |||
| 488 | $timeout = $1 * 86400; | |||
| 489 | }; | |||
| 490 | $timeout = 600 unless $timeout; | |||
| 491 | ||||
| 492 | _version_check($type,$query); # spent 0.00147s making 1 calls to C4::Auth::_version_check | |||
| 493 | # state variables | |||
| 494 | my $loggedin = 0; | |||
| 495 | my %info; | |||
| 496 | my ( $userid, $cookie, $sessionID, $flags, $barshelves, $pubshelves ); | |||
| 497 | my $logout = $query->param('logout.x'); # spent 0.00003s making 1 calls to CGI::param | |||
| 498 | ||||
| 499 | if ( $userid = $ENV{'REMOTE_USER'} ) { # spent 0.00007s making 1 calls to CGI::cookie | |||
| 500 | # Using Basic Authentication, no cookies required | |||
| 501 | $cookie = $query->cookie( | |||
| 502 | -name => 'CGISESSID', | |||
| 503 | -value => '', | |||
| 504 | -expires => '' | |||
| 505 | ); | |||
| 506 | $loggedin = 1; | |||
| 507 | } | |||
| 508 | elsif ( $sessionID = $query->cookie("CGISESSID")) { # assignment, not comparison | |||
| 509 | my $session = get_session($sessionID); | |||
| 510 | C4::Context->_new_userenv($sessionID); | |||
| 511 | my ($ip, $lasttime, $sessiontype); | |||
| 512 | if ($session){ | |||
| 513 | C4::Context::set_userenv( | |||
| 514 | $session->param('number'), $session->param('id'), | |||
| 515 | $session->param('cardnumber'), $session->param('firstname'), | |||
| 516 | $session->param('surname'), $session->param('branch'), | |||
| 517 | $session->param('branchname'), $session->param('flags'), | |||
| 518 | $session->param('emailaddress'), $session->param('branchprinter') | |||
| 519 | ); | |||
| 520 | C4::Context::set_shelves_userenv('bar',$session->param('barshelves')); | |||
| 521 | C4::Context::set_shelves_userenv('pub',$session->param('pubshelves')); | |||
| 522 | $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ; | |||
| 523 | $ip = $session->param('ip'); | |||
| 524 | $lasttime = $session->param('lasttime'); | |||
| 525 | $userid = $session->param('id'); | |||
| 526 | $sessiontype = $session->param('sessiontype'); | |||
| 527 | } | |||
| 528 | ||||
| 529 | if ( ($query->param('koha_login_context')) && ($query->param('userid') ne $session->param('id')) ) { | |||
| 530 | #if a user enters an id ne to the id in the current session, we need to log them in... | |||
| 531 | #first we need to clear the anonymous session... | |||
| 532 | $debug and warn "query id = " . $query->param('userid') . " but session id = " . $session->param('id'); | |||
| 533 | $session->flush; | |||
| 534 | $session->delete(); | |||
| 535 | C4::Context->_unset_userenv($sessionID); | |||
| 536 | $sessionID = undef; | |||
| 537 | $userid = undef; | |||
| 538 | } | |||
| 539 | elsif ($logout) { | |||
| 540 | # voluntary logout the user | |||
| 541 | $session->flush; | |||
| 542 | $session->delete(); | |||
| 543 | C4::Context->_unset_userenv($sessionID); | |||
| 544 | _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime); | |||
| 545 | $sessionID = undef; | |||
| 546 | $userid = undef; | |||
| 547 | } | |||
| 548 | elsif ( $lasttime < time() - $timeout ) { | |||
| 549 | # timed logout | |||
| 550 | $info{'timed_out'} = 1; | |||
| 551 | $session->delete(); | |||
| 552 | C4::Context->_unset_userenv($sessionID); | |||
| 553 | _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime); | |||
| 554 | $userid = undef; | |||
| 555 | $sessionID = undef; | |||
| 556 | } | |||
| 557 | elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) { | |||
| 558 | # Different ip than originally logged in from | |||
| 559 | $info{'oldip'} = $ip; | |||
| 560 | $info{'newip'} = $ENV{'REMOTE_ADDR'}; | |||
| 561 | $info{'different_ip'} = 1; | |||
| 562 | $session->delete(); | |||
| 563 | C4::Context->_unset_userenv($sessionID); | |||
| 564 | _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'}); | |||
| 565 | $sessionID = undef; | |||
| 566 | $userid = undef; | |||
| 567 | } | |||
| 568 | else { | |||
| 569 | $cookie = $query->cookie( CGISESSID => $session->id ); | |||
| 570 | $session->param('lasttime',time()); | |||
| 571 | unless ( $sessiontype eq 'anon' ) { #if this is an anonymous session, we want to update the session, but not behave as if they are logged in... | |||
| 572 | $flags = haspermission( $dbh, $userid, $flagsrequired ); | |||
| 573 | if ($flags) { | |||
| 574 | $loggedin = 1; | |||
| 575 | } else { | |||
| 576 | $info{'nopermission'} = 1; | |||
| 577 | } | |||
| 578 | } | |||
| 579 | } | |||
| 580 | } | |||
| 581 | 5 | 0.00007 | 0.00001 | unless ($userid || $sessionID) { |
| 582 | #we initiate a session prior to checking for a username to allow for anonymous sessions... | |||
| 583 | my $session = get_session("") or die "Auth ERROR: Cannot get_session()"; # spent 0.00670s making 1 calls to C4::Auth::get_session | |||
| 584 | my $sessionID = $session->id; # spent 0.00003s making 1 calls to CGI::Session::id | |||
| 585 | C4::Context->_new_userenv($sessionID); # spent 0.00002s making 1 calls to C4::Context::_new_userenv | |||
| 586 | $cookie = $query->cookie(CGISESSID => $sessionID); # spent 0.00025s making 1 calls to CGI::cookie | |||
| 587 | 7 | 0.00005 | 8e-06 | if ( $userid = $query->param('userid') ) { # spent 0.00003s making 1 calls to CGI::param |
| 588 | my $password = $query->param('password'); | |||
| 589 | my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password ); | |||
| 590 | if ($return) { | |||
| 591 | _session_log(sprintf "%20s from %16s logged in at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime); | |||
| 592 | if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) { | |||
| 593 | $loggedin = 1; | |||
| 594 | } | |||
| 595 | else { | |||
| 596 | $info{'nopermission'} = 1; | |||
| 597 | C4::Context->_unset_userenv($sessionID); | |||
| 598 | } | |||
| 599 | ||||
| 600 | my ($borrowernumber, $firstname, $surname, $userflags, | |||
| 601 | $branchcode, $branchname, $branchprinter, $emailaddress); | |||
| 602 | ||||
| 603 | if ( $return == 1 ) { | |||
| 604 | my $select = " | |||
| 605 | SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode, | |||
| 606 | branches.branchname as branchname, | |||
| 607 | branches.branchprinter as branchprinter, | |||
| 608 | ||||
| 609 | FROM borrowers | |||
| 610 | LEFT JOIN branches on borrowers.branchcode=branches.branchcode | |||
| 611 | "; | |||
| 612 | my $sth = $dbh->prepare("$select where userid=?"); | |||
| 613 | $sth->execute($userid); | |||
| 614 | unless ($sth->rows) { | |||
| 615 | $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n"; | |||
| 616 | $sth = $dbh->prepare("$select where cardnumber=?"); | |||
| 617 | $sth->execute($cardnumber); | |||
| 618 | unless ($sth->rows) { | |||
| 619 | $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n"; | |||
| 620 | $sth->execute($userid); | |||
| 621 | unless ($sth->rows) { | |||
| 622 | $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n"; | |||
| 623 | } | |||
| 624 | } | |||
| 625 | } | |||
| 626 | if ($sth->rows) { | |||
| 627 | ($borrowernumber, $firstname, $surname, $userflags, | |||
| 628 | $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow; | |||
| 629 | $debug and print STDERR "AUTH_3 results: " . | |||
| 630 | "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n"; | |||
| 631 | } else { | |||
| 632 | print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n"; | |||
| 633 | } | |||
| 634 | ||||
| 635 | # launch a sequence to check if we have a ip for the branch, i | |||
| 636 | # if we have one we replace the branchcode of the userenv by the branch bound in the ip. | |||
| 637 | ||||
| 638 | my $ip = $ENV{'REMOTE_ADDR'}; | |||
| 639 | # if they specify at login, use that | |||
| 640 | if ($query->param('branch')) { | |||
| 641 | $branchcode = $query->param('branch'); | |||
| 642 | $branchname = GetBranchName($branchcode); | |||
| 643 | } | |||
| 644 | my $branches = GetBranches(); | |||
| 645 | if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){ | |||
| 646 | # we have to check they are coming from the right ip range | |||
| 647 | my $domain = $branches->{$branchcode}->{'branchip'}; | |||
| 648 | if ($ip !~ /^$domain/){ | |||
| 649 | $loggedin=0; | |||
| 650 | $info{'wrongip'} = 1; | |||
| 651 | } | |||
| 652 | } | |||
| 653 | ||||
| 654 | my @branchesloop; | |||
| 655 | foreach my $br ( keys %$branches ) { | |||
| 656 | # now we work with the treatment of ip | |||
| 657 | my $domain = $branches->{$br}->{'branchip'}; | |||
| 658 | if ( $domain && $ip =~ /^$domain/ ) { | |||
| 659 | $branchcode = $branches->{$br}->{'branchcode'}; | |||
| 660 | ||||
| 661 | # new op dev : add the branchprinter and branchname in the cookie | |||
| 662 | $branchprinter = $branches->{$br}->{'branchprinter'}; | |||
| 663 | $branchname = $branches->{$br}->{'branchname'}; | |||
| 664 | } | |||
| 665 | } | |||
| 666 | $session->param('number',$borrowernumber); | |||
| 667 | $session->param('id',$userid); | |||
| 668 | $session->param('cardnumber',$cardnumber); | |||
| 669 | $session->param('firstname',$firstname); | |||
| 670 | $session->param('surname',$surname); | |||
| 671 | $session->param('branch',$branchcode); | |||
| 672 | $session->param('branchname',$branchname); | |||
| 673 | $session->param('flags',$userflags); | |||
| 674 | $session->param('emailaddress',$emailaddress); | |||
| 675 | $session->param('ip',$session->remote_addr()); | |||
| 676 | $session->param('lasttime',time()); | |||
| 677 | $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ; | |||
| 678 | } | |||
| 679 | elsif ( $return == 2 ) { | |||
| 680 | #We suppose the user is the superlibrarian | |||
| 681 | $borrowernumber = 0; | |||
| 682 | $session->param('number',0); | |||
| 683 | $session->param('id',C4::Context->config('user')); | |||
| 684 | $session->param('cardnumber',C4::Context->config('user')); | |||
| 685 | $session->param('firstname',C4::Context->config('user')); | |||
| 686 | $session->param('surname',C4::Context->config('user')); | |||
| 687 | $session->param('branch','NO_LIBRARY_SET'); | |||
| 688 | $session->param('branchname','NO_LIBRARY_SET'); | |||
| 689 | $session->param('flags',1); | |||
| 690 | $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress')); | |||
| 691 | $session->param('ip',$session->remote_addr()); | |||
| 692 | $session->param('lasttime',time()); | |||
| 693 | } | |||
| 694 | C4::Context::set_userenv( | |||
| 695 | $session->param('number'), $session->param('id'), | |||
| 696 | $session->param('cardnumber'), $session->param('firstname'), | |||
| 697 | $session->param('surname'), $session->param('branch'), | |||
| 698 | $session->param('branchname'), $session->param('flags'), | |||
| 699 | $session->param('emailaddress'), $session->param('branchprinter') | |||
| 700 | ); | |||
| 701 | ||||
| 702 | # Grab borrower's shelves and add to the session... | |||
| 703 | $barshelves = GetShelvesSummary($borrowernumber,2,10); | |||
| 704 | $session->param('barshelves', $barshelves); | |||
| 705 | C4::Context::set_shelves_userenv('bar',$barshelves); | |||
| 706 | ||||
| 707 | # Grab the public shelves and add to the session... | |||
| 708 | $pubshelves = GetShelvesSummary(0,2,10); | |||
| 709 | $session->param('pubshelves', $pubshelves); | |||
| 710 | C4::Context::set_shelves_userenv('pub',$pubshelves); | |||
| 711 | } | |||
| 712 | else { | |||
| 713 | if ($userid) { | |||
| 714 | $info{'invalid_username_or_password'} = 1; | |||
| 715 | C4::Context->_unset_userenv($sessionID); | |||
| 716 | } | |||
| 717 | } | |||
| 718 | } # END if ( $userid = $query->param('userid') ) | |||
| 719 | elsif ($type eq "opac") { | |||
| 720 | # if we are here this is an anonymous session; add public lists to it and a few other items... | |||
| 721 | # anonymous sessions are created only for the OPAC | |||
| 722 | $debug and warn "Initiating an anonymous session..."; | |||
| 723 | ||||
| 724 | # Grab the public shelves and add to the session... | |||
| 725 | $pubshelves = GetShelvesSummary(0,2,10); # spent 0.00039s making 1 calls to C4::VirtualShelves::GetShelvesSummary | |||
| 726 | $session->param('pubshelves', $pubshelves); # spent 0.00006s making 1 calls to CGI::Session::param | |||
| 727 | C4::Context::set_shelves_userenv('pub',$pubshelves); # spent 0.00002s making 1 calls to C4::Context::set_shelves_userenv | |||
| 728 | ||||
| 729 | # setting a couple of other session vars... | |||
| 730 | $session->param('ip',$session->remote_addr()); # spent 0.00005s making 1 calls to CGI::Session::param
# spent 9e-06s making 1 calls to CGI::Session::remote_addr | |||
| 731 | $session->param('lasttime',time()); # spent 0.00005s making 1 calls to CGI::Session::param | |||
| 732 | $session->param('sessiontype','anon'); # spent 0.00005s making 1 calls to CGI::Session::param | |||
| 733 | } | |||
| 734 | } # END unless ($userid) | |||
| 735 | my $insecure = C4::Context->boolean_preference('insecure'); # spent 0.00039s making 1 calls to C4::Context::boolean_preference | |||
| 736 | ||||
| 737 | # finished authentification, now respond | |||
| 738 | 2 | 8e-06 | 4e-06 | if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) ) |
| 739 | { | |||
| 740 | # successful login | |||
| 741 | 1 | 1e-06 | 1e-06 | unless ($cookie) { |
| 742 | $cookie = $query->cookie( CGISESSID => '' ); | |||
| 743 | } | |||
| 744 | return ( $userid, $cookie, $sessionID, $flags ); | |||
| 745 | } | |||
| 746 | ||||
| 747 | # | |||
| 748 | # | |||
| 749 | # AUTH rejected, show the login/password template, after checking the DB. | |||
| 750 | # | |||
| 751 | # | |||
| 752 | ||||
| 753 | # get the inputs from the incoming query | |||
| 754 | my @inputs = (); | |||
| 755 | foreach my $name ( param $query) { | |||
| 756 | (ne |