Adding experimental OPACShelfBrowser feature -- to try, create a new syspref called...
[koha.git] / C4 / Auth.pm
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);
45     $ldap = C4::Context->config('useldapserver') || 0;
46     if ($ldap) {
47         require C4::Auth_with_ldap;             # no import
48         import  C4::Auth_with_ldap qw(checkpw_ldap);
49     }
50 }
51
52 =head1 NAME
53
54 C4::Auth - Authenticates Koha users
55
56 =head1 SYNOPSIS
57
58   use CGI;
59   use C4::Auth;
60
61   my $query = new CGI;
62
63   my ($template, $borrowernumber, $cookie) 
64     = get_template_and_user(
65         {
66             template_name   => "opac-main.tmpl",
67             query           => $query,
68       type            => "opac",
69       authnotrequired => 1,
70       flagsrequired   => {borrow => 1},
71   }
72     );
73
74   print $query->header(
75     -type => 'utf-8',
76     -cookie => $cookie
77   ), $template->output;
78
79
80 =head1 DESCRIPTION
81
82     The main function of this module is to provide
83     authentification. However the get_template_and_user function has
84     been provided so that a users login information is passed along
85     automatically. This gets loaded into the template.
86
87 =head1 FUNCTIONS
88
89 =over 2
90
91 =item get_template_and_user
92
93     my ($template, $borrowernumber, $cookie)
94         = get_template_and_user(
95           {
96             template_name   => "opac-main.tmpl",
97             query           => $query,
98             type            => "opac",
99             authnotrequired => 1,
100             flagsrequired   => {borrow => 1},
101           }
102         );
103
104     This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
105     to C<&checkauth> (in this module) to perform authentification.
106     See C<&checkauth> for an explanation of these parameters.
107
108     The C<template_name> is then used to find the correct template for
109     the page. The authenticated users details are loaded onto the
110     template in the HTML::Template LOOP variable C<USER_INFO>. Also the
111     C<sessionID> is passed to the template. This can be used in templates
112     if cookies are disabled. It needs to be put as and input to every
113     authenticated page.
114
115     More information on the C<gettemplate> sub can be found in the
116     Output.pm module.
117
118 =cut
119
120 sub get_template_and_user {
121     my $in       = shift;
122     my $template =
123       gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
124     my ( $user, $cookie, $sessionID, $flags ) = checkauth(
125         $in->{'query'},
126         $in->{'authnotrequired'},
127         $in->{'flagsrequired'},
128         $in->{'type'}
129     ) unless ($in->{'template_name'}=~/maintenance/);
130
131     my $borrowernumber;
132     my $insecure = C4::Context->preference('insecure');
133     if ($user or $insecure) {
134
135         # load the template variables for stylesheets and JavaScript
136         $template->param( css_libs => $in->{'css_libs'} );
137         $template->param( css_module => $in->{'css_module'} );
138         $template->param( css_page => $in->{'css_page'} );
139         $template->param( css_widgets => $in->{'css_widgets'} );
140
141         $template->param( js_libs => $in->{'js_libs'} );
142         $template->param( js_module => $in->{'js_module'} );
143         $template->param( js_page => $in->{'js_page'} );
144         $template->param( js_widgets => $in->{'js_widgets'} );
145
146         # user info
147         $template->param( loggedinusername => $user );
148         $template->param( sessionID        => $sessionID );
149                 my $shelves;
150                 if ($shelves = C4::Context->get_shelves_userenv()) {
151                 $template->param( barshelves     => scalar (@$shelves));
152                 $template->param( barshelvesloop => $shelves);
153                 }
154
155         $borrowernumber = getborrowernumber($user);
156         my ( $borr, $alternativeflags ) =
157           GetMemberDetails( $borrowernumber );
158         my @bordat;
159         $bordat[0] = $borr;
160         $template->param( "USER_INFO" => \@bordat );
161
162         my @flagroots = qw(circulate catalogue parameters borrowers permissions reserveforothers borrow
163                             editcatalogue updatecharges management tools editauthorities serials reports);
164         # We are going to use the $flags returned by checkauth
165         # to create the template's parameters that will indicate
166         # which menus the user can access.
167         if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
168             $template->param( CAN_user_circulate        => 1 );
169             $template->param( CAN_user_catalogue        => 1 );
170             $template->param( CAN_user_parameters       => 1 );
171             $template->param( CAN_user_borrowers        => 1 );
172             $template->param( CAN_user_permission       => 1 );
173             $template->param( CAN_user_reserveforothers => 1 );
174             $template->param( CAN_user_borrow           => 1 );
175             $template->param( CAN_user_editcatalogue    => 1 );
176             $template->param( CAN_user_updatecharges     => 1 );
177             $template->param( CAN_user_acquisition      => 1 );
178             $template->param( CAN_user_management       => 1 );
179             $template->param( CAN_user_tools            => 1 ); 
180             $template->param( CAN_user_editauthorities  => 1 );
181             $template->param( CAN_user_serials          => 1 );
182             $template->param( CAN_user_reports          => 1 );
183             $template->param( CAN_user_staffaccess      => 1 );
184         }
185
186         if ( $flags && $flags->{circulate} == 1 ) {
187             $template->param( CAN_user_circulate => 1 );
188         }
189
190         if ( $flags && $flags->{catalogue} == 1 ) {
191             $template->param( CAN_user_catalogue => 1 );
192         }
193
194         if ( $flags && $flags->{parameters} == 1 ) {
195             $template->param( CAN_user_parameters => 1 );
196             $template->param( CAN_user_management => 1 );
197         }
198
199         if ( $flags && $flags->{borrowers} == 1 ) {
200             $template->param( CAN_user_borrowers => 1 );
201         }
202
203         if ( $flags && $flags->{permissions} == 1 ) {
204             $template->param( CAN_user_permission => 1 );
205         }
206
207         if ( $flags && $flags->{reserveforothers} == 1 ) {
208             $template->param( CAN_user_reserveforothers => 1 );
209         }
210
211         if ( $flags && $flags->{borrow} == 1 ) {
212             $template->param( CAN_user_borrow => 1 );
213         }
214
215         if ( $flags && $flags->{editcatalogue} == 1 ) {
216             $template->param( CAN_user_editcatalogue => 1 );
217         }
218
219         if ( $flags && $flags->{updatecharges} == 1 ) {
220             $template->param( CAN_user_updatecharges => 1 );
221         }
222
223         if ( $flags && $flags->{acquisition} == 1 ) {
224             $template->param( CAN_user_acquisition => 1 );
225         }
226
227         if ( $flags && $flags->{tools} == 1 ) {
228             $template->param( CAN_user_tools => 1 );
229         }
230   
231         if ( $flags && $flags->{editauthorities} == 1 ) {
232             $template->param( CAN_user_editauthorities => 1 );
233         }
234     
235         if ( $flags && $flags->{serials} == 1 ) {
236             $template->param( CAN_user_serials => 1 );
237         }
238
239         if ( $flags && $flags->{reports} == 1 ) {
240             $template->param( CAN_user_reports => 1 );
241         }
242         if ( $flags && $flags->{staffaccess} == 1 ) {
243             $template->param( CAN_user_staffaccess => 1 );
244         }
245     }
246     if ( $in->{'type'} eq "intranet" ) {
247         $template->param(
248             intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
249             intranetstylesheet => C4::Context->preference("intranetstylesheet"),
250             IntranetNav        => C4::Context->preference("IntranetNav"),
251             intranetuserjs     => C4::Context->preference("intranetuserjs"),
252             TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
253             AmazonContent      => C4::Context->preference("AmazonContent"),
254             LibraryName        => C4::Context->preference("LibraryName"),
255             LoginBranchcode    => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
256             LoginBranchname    => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
257             LoginFirstname     => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
258             LoginSurname       => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
259             AutoLocation       => C4::Context->preference("AutoLocation"),
260             hide_marc          => C4::Context->preference("hide_marc"),
261             patronimages       => C4::Context->preference("patronimages"),
262             "BiblioDefaultView".C4::Context->preference("IntranetBiblioDefaultView") => 1,
263             advancedMARCEditor      => C4::Context->preference("advancedMARCEditor"),
264             suggestion              => C4::Context->preference("suggestion"),
265             virtualshelves          => C4::Context->preference("virtualshelves"),
266             LibraryName             => C4::Context->preference("LibraryName"),
267             KohaAdminEmailAddress   => "" . C4::Context->preference("KohaAdminEmailAddress"),
268             IntranetmainUserblock   => C4::Context->preference("IntranetmainUserblock"),
269             IndependantBranches     => C4::Context->preference("IndependantBranches"),
270                         CircAutocompl => C4::Context->preference("CircAutocompl"),
271                         yuipath => C4::Context->preference("yuipath"),
272                         FRBRizeEditions => C4::Context->preference("FRBRizeEditions"),
273                         AmazonSimilarItems => C4::Context->preference("AmazonSimilarItems"),
274                         'item-level_itypes' => C4::Context->preference('item-level_itypes'),
275                         canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'),
276                         intranetreadinghistory => C4::Context->preference("intranetreadinghistory"),
277                         noItemTypeImages => C4::Context->preference("noItemTypeImages"),
278         );
279     }
280     else {
281         warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
282         my $LibraryNameTitle = C4::Context->preference("LibraryName");
283         $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
284         $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
285   $template->param(
286             KohaAdminEmailAddress  => "" . C4::Context->preference("KohaAdminEmailAddress"),
287             AnonSuggestions =>  "" . C4::Context->preference("AnonSuggestions"),
288             suggestion             => "" . C4::Context->preference("suggestion"),
289             virtualshelves         => "" . C4::Context->preference("virtualshelves"),
290             OpacNav                => "" . C4::Context->preference("OpacNav"),
291             opacheader             => "" . C4::Context->preference("opacheader"),
292             opaccredits            => "" . C4::Context->preference("opaccredits"),
293             opacsmallimage         => "" . C4::Context->preference("opacsmallimage"),
294             opaclargeimage         => "" . C4::Context->preference("opaclargeimage"),
295             opaclayoutstylesheet   => "". C4::Context->preference("opaclayoutstylesheet"),
296             opaccolorstylesheet    => "". C4::Context->preference("opaccolorstylesheet"),
297             opaclanguagesdisplay   => "". C4::Context->preference("opaclanguagesdisplay"),
298             opacuserlogin          => "" . C4::Context->preference("opacuserlogin"),
299             opacbookbag            => "" . C4::Context->preference("opacbookbag"),
300             TemplateEncoding       => "". C4::Context->preference("TemplateEncoding"),
301             AmazonContent          => "" . C4::Context->preference("AmazonContent"),
302             OPACShelfBrowser       => "". C4::Context->preference("OPACShelfBrowser"),
303             OPACAmazonSimilarItems => "" . C4::Context->preference("OPACAmazonSimilarItems"),
304             LibraryName            => "" . C4::Context->preference("LibraryName"),
305             LibraryNameTitle       => "" . $LibraryNameTitle,
306             LoginBranchcode        => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
307             LoginBranchname        => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", 
308             LoginFirstname        => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
309             LoginSurname        => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
310             OpacPasswordChange     => C4::Context->preference("OpacPasswordChange"),
311             opacreadinghistory     => C4::Context->preference("opacreadinghistory"),
312             opacuserjs             => C4::Context->preference("opacuserjs"),
313             OpacCloud              => C4::Context->preference("OpacCloud"),
314             OpacTopissue           => C4::Context->preference("OpacTopissue"),
315             OpacAuthorities        => C4::Context->preference("OpacAuthorities"),
316             OpacBrowser            => C4::Context->preference("OpacBrowser"),
317             RequestOnOpac          => C4::Context->preference("RequestOnOpac"),
318             reviewson              => C4::Context->preference("reviewson"),
319             hide_marc              => C4::Context->preference("hide_marc"),
320             patronimages           => C4::Context->preference("patronimages"),
321             mylibraryfirst   => C4::Context->preference("SearchMyLibraryFirst"),
322             "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
323             OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"),
324             'item-level_itypes' => C4::Context->preference('item-level_itypes'),
325                         yuipath => C4::Context->preference("yuipath"),
326         );
327     }
328         $template->param(listloop=>[{shelfname=>"Freelist", shelfnumber=>110}]);
329     return ( $template, $borrowernumber, $cookie, $flags);
330 }
331
332 =item checkauth
333
334   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
335
336 Verifies that the user is authorized to run this script.  If
337 the user is authorized, a (userid, cookie, session-id, flags)
338 quadruple is returned.  If the user is not authorized but does
339 not have the required privilege (see $flagsrequired below), it
340 displays an error page and exits.  Otherwise, it displays the
341 login page and exits.
342
343 Note that C<&checkauth> will return if and only if the user
344 is authorized, so it should be called early on, before any
345 unfinished operations (e.g., if you've opened a file, then
346 C<&checkauth> won't close it for you).
347
348 C<$query> is the CGI object for the script calling C<&checkauth>.
349
350 The C<$noauth> argument is optional. If it is set, then no
351 authorization is required for the script.
352
353 C<&checkauth> fetches user and session information from C<$query> and
354 ensures that the user is authorized to run scripts that require
355 authorization.
356
357 The C<$flagsrequired> argument specifies the required privileges
358 the user must have if the username and password are correct.
359 It should be specified as a reference-to-hash; keys in the hash
360 should be the "flags" for the user, as specified in the Members
361 intranet module. Any key specified must correspond to a "flag"
362 in the userflags table. E.g., { circulate => 1 } would specify
363 that the user must have the "circulate" privilege in order to
364 proceed. To make sure that access control is correct, the
365 C<$flagsrequired> parameter must be specified correctly.
366
367 The C<$type> argument specifies whether the template should be
368 retrieved from the opac or intranet directory tree.  "opac" is
369 assumed if it is not specified; however, if C<$type> is specified,
370 "intranet" is assumed if it is not "opac".
371
372 If C<$query> does not have a valid session ID associated with it
373 (i.e., the user has not logged in) or if the session has expired,
374 C<&checkauth> presents the user with a login page (from the point of
375 view of the original script, C<&checkauth> does not return). Once the
376 user has authenticated, C<&checkauth> restarts the original script
377 (this time, C<&checkauth> returns).
378
379 The login page is provided using a HTML::Template, which is set in the
380 systempreferences table or at the top of this file. The variable C<$type>
381 selects which template to use, either the opac or the intranet 
382 authentification template.
383
384 C<&checkauth> returns a user ID, a cookie, and a session ID. The
385 cookie should be sent back to the browser; it verifies that the user
386 has authenticated.
387
388 =cut
389
390 sub _version_check ($$) {
391     my $type = shift;
392     my $query = shift;
393     my $version;
394     # If Version syspref is unavailable, it means Koha is beeing installed,
395     # and so we must redirect to OPAC maintenance page or to the WebInstaller
396     #warn "about to check version";
397     unless ($version = C4::Context->preference('Version')) {    # assignment, not comparison
398       if ($type ne 'opac') {
399         warn "Install required, redirecting to Installer";
400         print $query->redirect("/cgi-bin/koha/installer/install.pl");
401       } 
402       else {
403         warn "OPAC Install required, redirecting to maintenance";
404         print $query->redirect("/cgi-bin/koha/maintenance.pl");
405       }
406       exit;
407     }
408
409     # check that database and koha version are the same
410     # there is no DB version, it's a fresh install,
411     # go to web installer
412     # there is a DB version, compare it to the code version
413     my $kohaversion=C4::Context::KOHAVERSION;
414     # remove the 3 last . to have a Perl number
415     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
416     $debug and print STDERR "kohaversion : $kohaversion\n";
417     if ($version < $kohaversion){
418         my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is "
419             . C4::Context->config("kohaversion");
420         if ($type ne 'opac'){
421             warn sprintf($warning, 'Installer');
422             print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
423         } else {
424             warn sprintf("OPAC: " . $warning, 'maintenance');
425             print $query->redirect("/cgi-bin/koha/maintenance.pl");
426         }       
427         exit;
428     }
429 }
430
431 sub _session_log {
432     (@_) or return 0;
433     open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog";
434     printf L join("\n",@_);
435     close L;
436 }
437
438 sub checkauth {
439     my $query = shift;
440         $debug and warn "Checking Auth";
441     # $authnotrequired will be set for scripts which will run without authentication
442     my $authnotrequired = shift;
443     my $flagsrequired   = shift;
444     my $type            = shift;
445     $type = 'opac' unless $type;
446
447     my $dbh     = C4::Context->dbh;
448     my $timeout = C4::Context->preference('timeout');
449     # days
450     if ($timeout =~ /(\d+)[dD]/) {
451         $timeout = $1 * 86400;
452     };
453     $timeout = 600 unless $timeout;
454
455     _version_check($type,$query);
456     # state variables
457     my $loggedin = 0;
458     my %info;
459     my ( $userid, $cookie, $sessionID, $flags, $shelves );
460     my $logout = $query->param('logout.x');
461     if ( $userid = $ENV{'REMOTE_USER'} ) {
462         # Using Basic Authentication, no cookies required
463         $cookie = $query->cookie(
464             -name    => 'CGISESSID',
465             -value   => '',
466             -expires => ''
467         );
468         $loggedin = 1;
469     }
470     elsif ( $sessionID = $query->cookie("CGISESSID")) {     # assignment, not comparison 
471         my $session = get_session($sessionID);
472         C4::Context->_new_userenv($sessionID);
473         my ($ip, $lasttime);
474         if ($session){
475             C4::Context::set_userenv(
476                 $session->param('number'),       $session->param('id'),
477                 $session->param('cardnumber'),   $session->param('firstname'),
478                 $session->param('surname'),      $session->param('branch'),
479                 $session->param('branchname'),   $session->param('flags'),
480                 $session->param('emailaddress'), $session->param('branchprinter')
481             );
482             C4::Context::set_shelves_userenv($session->param('shelves'));
483             $debug and printf STDERR "AUTH_SESSION: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
484             $ip       = $session->param('ip');
485             $lasttime = $session->param('lasttime');
486             $userid   = $session->param('id');
487         }
488     
489         if ($logout) {
490             # voluntary logout the user
491             $session->flush;      
492             $session->delete();
493             C4::Context->_unset_userenv($sessionID);
494             _session_log(sprintf "%20s from %16s logged out at %30s (manually).\n", $userid,$ip,localtime);
495             $sessionID = undef;
496             $userid    = undef;
497         }
498                 elsif ( $lasttime < time() - $timeout ) {
499                         # timed logout
500                         $info{'timed_out'} = 1;
501                         $session->delete();
502                         C4::Context->_unset_userenv($sessionID);
503                         _session_log(sprintf "%20s from %16s logged out at %30s (inactivity).\n", $userid,$ip,localtime);
504                         $userid    = undef;
505                         $sessionID = undef;
506                 }
507                 elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
508                         # Different ip than originally logged in from
509                         $info{'oldip'}        = $ip;
510                         $info{'newip'}        = $ENV{'REMOTE_ADDR'};
511                         $info{'different_ip'} = 1;
512                         $session->delete();
513                         C4::Context->_unset_userenv($sessionID);
514                         _session_log(sprintf "%20s from %16s logged out at %30s (ip changed to %16s).\n", $userid,$ip,localtime, $info{'newip'});
515                         $sessionID = undef;
516                         $userid    = undef;
517                 }
518                 else {
519                         $cookie = $query->cookie( CGISESSID => $session->id );
520                         $session->param('lasttime',time());
521                         $flags = haspermission( $dbh, $userid, $flagsrequired );
522                         if ($flags) {
523                                 $loggedin = 1;
524                         } else {
525                                 $info{'nopermission'} = 1;
526                         }
527                 }
528     }
529     unless ($userid) {
530         my $session = get_session("") or die "Auth ERROR: Cannot get_session()";
531         my $sessionID = $session->id;
532         $userid    = $query->param('userid');
533         my $password = $query->param('password');
534         C4::Context->_new_userenv($sessionID);
535         my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
536         if ($return) {
537             _session_log(sprintf "%20s from %16s logged in  at %30s.\n", $userid,$ENV{'REMOTE_ADDR'},localtime);
538             $cookie = $query->cookie(CGISESSID => $sessionID);
539             if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
540                                 $loggedin = 1;
541             }
542             else {
543                 $info{'nopermission'} = 1;
544                 C4::Context->_unset_userenv($sessionID);
545             }
546
547                         my ($borrowernumber, $firstname, $surname, $userflags,
548                                 $branchcode, $branchname, $branchprinter, $emailaddress);
549
550             if ( $return == 1 ) {
551                 my $select = "
552                 SELECT borrowernumber, firstname, surname, flags, borrowers.branchcode, 
553                         branches.branchname    as branchname, 
554                         branches.branchprinter as branchprinter, 
555                         email 
556                 FROM borrowers 
557                 LEFT JOIN branches on borrowers.branchcode=branches.branchcode
558                 ";
559                 my $sth = $dbh->prepare("$select where userid=?");
560                 $sth->execute($userid);
561                                 unless ($sth->rows) {
562                         $debug and print STDERR "AUTH_1: no rows for userid='$userid'\n";
563                                         $sth = $dbh->prepare("$select where cardnumber=?");
564                     $sth->execute($cardnumber);
565                                         unless ($sth->rows) {
566                                 $debug and print STDERR "AUTH_2a: no rows for cardnumber='$cardnumber'\n";
567                         $sth->execute($userid);
568                                                 unless ($sth->rows) {
569                                         $debug and print STDERR "AUTH_2b: no rows for userid='$userid' AS cardnumber\n";
570                                                 }
571                                         }
572                                 }
573                 if ($sth->rows) {
574                     ($borrowernumber, $firstname, $surname, $userflags,
575                         $branchcode, $branchname, $branchprinter, $emailaddress) = $sth->fetchrow;
576                                         $debug and print STDERR "AUTH_3 results: " .
577                                                 "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress\n";
578                                 } else {
579                                         print STDERR "AUTH_3: no results for userid='$userid', cardnumber='$cardnumber'.\n";
580                                 }
581
582 # launch a sequence to check if we have a ip for the branch, i
583 # if we have one we replace the branchcode of the userenv by the branch bound in the ip.
584
585                 my $ip       = $ENV{'REMOTE_ADDR'};
586                 # if they specify at login, use that
587                 if ($query->param('branch')) {
588                     $branchcode  = $query->param('branch');
589                     $branchname = GetBranchName($branchcode);
590                 }
591                 my $branches = GetBranches();
592                 if (C4::Context->boolean_preference('IndependantBranches') && C4::Context->boolean_preference('Autolocation')){
593                                     # we have to check they are coming from the right ip range
594                                         my $domain = $branches->{$branchcode}->{'branchip'};
595                                         if ($ip !~ /^$domain/){
596                                                 $loggedin=0;
597                                                 $info{'wrongip'} = 1;
598                                         }
599                                 }
600
601                 my @branchesloop;
602                 foreach my $br ( keys %$branches ) {
603                     #     now we work with the treatment of ip
604                     my $domain = $branches->{$br}->{'branchip'};
605                     if ( $domain && $ip =~ /^$domain/ ) {
606                         $branchcode = $branches->{$br}->{'branchcode'};
607
608                         # new op dev : add the branchprinter and branchname in the cookie
609                         $branchprinter = $branches->{$br}->{'branchprinter'};
610                         $branchname    = $branches->{$br}->{'branchname'};
611                     }
612                 }
613                 $session->param('number',$borrowernumber);
614                 $session->param('id',$userid);
615                 $session->param('cardnumber',$cardnumber);
616                 $session->param('firstname',$firstname);
617                 $session->param('surname',$surname);
618                 $session->param('branch',$branchcode);
619                 $session->param('branchname',$branchname);
620                 $session->param('flags',$userflags);
621                 $session->param('emailaddress',$emailaddress);
622                 $session->param('ip',$session->remote_addr());
623                 $session->param('lasttime',time());
624                 $debug and printf STDERR "AUTH_4: (%s)\t%s %s - %s\n", map {$session->param($_)} qw(cardnumber firstname surname branch) ;
625             }
626             elsif ( $return == 2 ) {
627                 #We suppose the user is the superlibrarian
628                                 $borrowernumber = 0;
629                 $session->param('number',0);
630                 $session->param('id',C4::Context->config('user'));
631                 $session->param('cardnumber',C4::Context->config('user'));
632                 $session->param('firstname',C4::Context->config('user'));
633                 $session->param('surname',C4::Context->config('user'));
634                 $session->param('branch','NO_LIBRARY_SET');
635                 $session->param('branchname','NO_LIBRARY_SET');
636                 $session->param('flags',1);
637                 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
638                 $session->param('ip',$session->remote_addr());
639                 $session->param('lasttime',time());
640             }
641             C4::Context::set_userenv(
642                 $session->param('number'),       $session->param('id'),
643                 $session->param('cardnumber'),   $session->param('firstname'),
644                 $session->param('surname'),      $session->param('branch'),
645                 $session->param('branchname'),   $session->param('flags'),
646                 $session->param('emailaddress'), $session->param('branchprinter')
647             );
648                         $shelves = GetShelvesSummary($borrowernumber,2,10);
649                         $session->param('shelves', $shelves);
650                         C4::Context::set_shelves_userenv($shelves);
651         }
652         else {
653             if ($userid) {
654                 $info{'invalid_username_or_password'} = 1;
655                 C4::Context->_unset_userenv($sessionID);
656             }
657
658         }
659     }   # END unless ($userid)
660     my $insecure = C4::Context->boolean_preference('insecure');
661
662     # finished authentification, now respond
663     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
664     {
665         # successful login
666         unless ($cookie) {
667             $cookie = $query->cookie( CGISESSID => '' );
668         }
669         return ( $userid, $cookie, $sessionID, $flags );
670     }
671
672 #
673 #
674 # AUTH rejected, show the login/password template, after checking the DB.
675 #
676 #
677     
678     # get the inputs from the incoming query
679     my @inputs = ();
680     foreach my $name ( param $query) {
681         (next) if ( $name eq 'userid' || $name eq 'password' );
682         my $value = $query->param($name);
683         push @inputs, { name => $name, value => $value };
684     }
685     # get the branchloop, which we need for authentication
686     my $branches = GetBranches();
687     my @branch_loop;
688     for my $branch_hash (sort keys %$branches) {
689                 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
690     }
691
692     my $template_name = ( $type eq 'opac' ) ? 'opac-auth.tmpl' : 'auth.tmpl';
693     my $template = gettemplate( $template_name, $type, $query );
694     $template->param(branchloop => \@branch_loop,);
695     $template->param(
696     login        => 1,
697         INPUTS               => \@inputs,
698         suggestion           => C4::Context->preference("suggestion"),
699         virtualshelves       => C4::Context->preference("virtualshelves"),
700         opaclargeimage       => C4::Context->preference("opaclargeimage"),
701         LibraryName          => C4::Context->preference("LibraryName"),
702         opacuserlogin        => C4::Context->preference("opacuserlogin"),
703         OpacNav              => C4::Context->preference("OpacNav"),
704         opaccredits          => C4::Context->preference("opaccredits"),
705         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
706         opacsmallimage       => C4::Context->preference("opacsmallimage"),
707         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
708         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
709         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
710         opacuserjs           => C4::Context->preference("opacuserjs"),
711         intranetcolorstylesheet =>
712                                                                 C4::Context->preference("intranetcolorstylesheet"),
713         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
714         IntranetNav        => C4::Context->preference("IntranetNav"),
715         intranetuserjs     => C4::Context->preference("intranetuserjs"),
716         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
717         IndependantBranches=> C4::Context->preference("IndependantBranches"),
718         AutoLocation       => C4::Context->preference("AutoLocation"),
719         yuipath            => C4::Context->preference("yuipath"),
720                 wrongip            => $info{'wrongip'}
721     );
722     
723     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
724
725     my $self_url = $query->url( -absolute => 1 );
726     $template->param(
727         url         => $self_url,
728         LibraryName => C4::Context->preference("LibraryName"),
729     );
730     $template->param( \%info );
731 #    $cookie = $query->cookie(CGISESSID => $session->id
732 #   );
733     print $query->header(
734         -type   => 'text/html',
735         -charset => 'utf-8',
736         -cookie => $cookie
737       ),
738       $template->output;
739     exit;
740 }
741
742 =item check_api_auth
743
744   ($status, $cookie, $sessionId) = check_api_auth($query, $userflags);
745
746 Given a CGI query containing the parameters 'userid' and 'password' and/or a session
747 cookie, determine if the user has the privileges specified by C<$userflags>.
748
749 C<check_api_auth> is is meant for authenticating users of web services, and
750 consequently will always return and will not attempt to redirect the user
751 agent.
752
753 If a valid session cookie is already present, check_api_auth will return a status
754 of "ok", the cookie, and the Koha session ID.
755
756 If no session cookie is present, check_api_auth will check the 'userid' and 'password
757 parameters and create a session cookie and Koha session if the supplied credentials
758 are OK.
759
760 Possible return values in C<$status> are:
761
762 =over 4
763
764 =item "ok" -- user authenticated; C<$cookie> and C<$sessionid> have valid values.
765
766 =item "failed" -- credentials are not correct; C<$cookie> and C<$sessionid> are undef
767
768 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
769
770 =item "expired -- session cookie has expired; API user should resubmit userid and password
771
772 =back
773
774 =cut
775
776 sub check_api_auth {
777     my $query = shift;
778     my $flagsrequired = shift;
779
780     my $dbh     = C4::Context->dbh;
781     my $timeout = C4::Context->preference('timeout');
782     $timeout = 600 unless $timeout;
783
784     unless (C4::Context->preference('Version')) {
785         # database has not been installed yet
786         return ("maintenance", undef, undef);
787     }
788     my $kohaversion=C4::Context::KOHAVERSION;
789     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
790     if (C4::Context->preference('Version') < $kohaversion) {
791         # database in need of version update; assume that
792         # no API should be called while databsae is in
793         # this condition.
794         return ("maintenance", undef, undef);
795     }
796
797     # FIXME -- most of what follows is a copy-and-paste
798     # of code from checkauth.  There is an obvious need
799     # for refactoring to separate the various parts of
800     # the authentication code, but as of 2007-11-19 this
801     # is deferred so as to not introduce bugs into the
802     # regular authentication code for Koha 3.0.
803
804     # see if we have a valid session cookie already
805     # however, if a userid parameter is present (i.e., from
806     # a form submission, assume that any current cookie
807     # is to be ignored
808     my $sessionID = undef;
809     unless ($query->param('userid')) {
810         $sessionID = $query->cookie("CGISESSID");
811     }
812     if ($sessionID) {
813         my $session = get_session($sessionID);
814         C4::Context->_new_userenv($sessionID);
815         if ($session) {
816             C4::Context::set_userenv(
817                 $session->param('number'),       $session->param('id'),
818                 $session->param('cardnumber'),   $session->param('firstname'),
819                 $session->param('surname'),      $session->param('branch'),
820                 $session->param('branchname'),   $session->param('flags'),
821                 $session->param('emailaddress'), $session->param('branchprinter')
822             );
823
824             my $ip = $session->param('ip');
825             my $lasttime = $session->param('lasttime');
826             my $userid = $session->param('id');
827             if ( $lasttime < time() - $timeout ) {
828                 # time out
829                 $session->delete();
830                 C4::Context->_unset_userenv($sessionID);
831                 $userid    = undef;
832                 $sessionID = undef;
833                 return ("expired", undef, undef);
834             } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
835                 # IP address changed
836                 $session->delete();
837                 C4::Context->_unset_userenv($sessionID);
838                 $userid    = undef;
839                 $sessionID = undef;
840                 return ("expired", undef, undef);
841             } else {
842                 my $cookie = $query->cookie( CGISESSID => $session->id );
843                 $session->param('lasttime',time());
844                 my $flags = haspermission( $dbh, $userid, $flagsrequired );
845                 if ($flags) {
846                     return ("ok", $cookie, $sessionID);
847                 } else {
848                     $session->delete();
849                     C4::Context->_unset_userenv($sessionID);
850                     $userid    = undef;
851                     $sessionID = undef;
852                     return ("failed", undef, undef);
853                 }
854             }
855         } else {
856             return ("expired", undef, undef);
857         }
858     } else {
859         # new login
860         my $userid = $query->param('userid');   
861         my $password = $query->param('password');   
862         unless ($userid and $password) {
863             # caller did something wrong, fail the authenticateion
864             return ("failed", undef, undef);
865         }
866         my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
867         if ($return and haspermission( $dbh, $userid, $flagsrequired)) {
868             my $session = get_session("");
869             return ("failed", undef, undef) unless $session;
870
871             my $sessionID = $session->id;
872             C4::Context->_new_userenv($sessionID);
873             my $cookie = $query->cookie(CGISESSID => $sessionID);
874             if ( $return == 1 ) {
875                 my (
876                     $borrowernumber, $firstname,  $surname,
877                     $userflags,      $branchcode, $branchname,
878                     $branchprinter,  $emailaddress
879                 );
880                 my $sth =
881                   $dbh->prepare(
882 "select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname,branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where userid=?"
883                   );
884                 $sth->execute($userid);
885                 (
886                     $borrowernumber, $firstname,  $surname,
887                     $userflags,      $branchcode, $branchname,
888                     $branchprinter,  $emailaddress
889                 ) = $sth->fetchrow if ( $sth->rows );
890
891                 unless ($sth->rows ) {
892                     my $sth = $dbh->prepare(
893 "select borrowernumber, firstname, surname, flags, borrowers.branchcode, branches.branchname as branchname, branches.branchprinter as branchprinter, email from borrowers left join branches on borrowers.branchcode=branches.branchcode where cardnumber=?"
894                       );
895                     $sth->execute($cardnumber);
896                     (
897                         $borrowernumber, $firstname,  $surname,
898                         $userflags,      $branchcode, $branchname,
899                         $branchprinter,  $emailaddress
900                     ) = $sth->fetchrow if ( $sth->rows );
901
902                     unless ( $sth->rows ) {
903                         $sth->execute($userid);
904                         (
905                             $borrowernumber, $firstname, $surname, $userflags,
906                             $branchcode, $branchname, $branchprinter, $emailaddress
907                         ) = $sth->fetchrow if ( $sth->rows );
908                     }
909                 }
910
911                 my $ip       = $ENV{'REMOTE_ADDR'};
912                 # if they specify at login, use that
913                 if ($query->param('branch')) {
914                     $branchcode  = $query->param('branch');
915                     $branchname = GetBranchName($branchcode);
916                 }
917                 my $branches = GetBranches();
918                 my @branchesloop;
919                 foreach my $br ( keys %$branches ) {
920                     #     now we work with the treatment of ip
921                     my $domain = $branches->{$br}->{'branchip'};
922                     if ( $domain && $ip =~ /^$domain/ ) {
923                         $branchcode = $branches->{$br}->{'branchcode'};
924
925                         # new op dev : add the branchprinter and branchname in the cookie
926                         $branchprinter = $branches->{$br}->{'branchprinter'};
927                         $branchname    = $branches->{$br}->{'branchname'};
928                     }
929                 }
930                 $session->param('number',$borrowernumber);
931                 $session->param('id',$userid);
932                 $session->param('cardnumber',$cardnumber);
933                 $session->param('firstname',$firstname);
934                 $session->param('surname',$surname);
935                 $session->param('branch',$branchcode);
936                 $session->param('branchname',$branchname);
937                 $session->param('flags',$userflags);
938                 $session->param('emailaddress',$emailaddress);
939                 $session->param('ip',$session->remote_addr());
940                 $session->param('lasttime',time());
941             } elsif ( $return == 2 ) {
942                 #We suppose the user is the superlibrarian
943                 $session->param('number',0);
944                 $session->param('id',C4::Context->config('user'));
945                 $session->param('cardnumber',C4::Context->config('user'));
946                 $session->param('firstname',C4::Context->config('user'));
947                 $session->param('surname',C4::Context->config('user'));
948                 $session->param('branch','NO_LIBRARY_SET');
949                 $session->param('branchname','NO_LIBRARY_SET');
950                 $session->param('flags',1);
951                 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
952                 $session->param('ip',$session->remote_addr());
953                 $session->param('lasttime',time());
954             } 
955             C4::Context::set_userenv(
956                 $session->param('number'),       $session->param('id'),
957                 $session->param('cardnumber'),   $session->param('firstname'),
958                 $session->param('surname'),      $session->param('branch'),
959                 $session->param('branchname'),   $session->param('flags'),
960                 $session->param('emailaddress'), $session->param('branchprinter')
961             );
962             return ("ok", $cookie, $sessionID);
963         } else {
964             return ("failed", undef, undef);
965         }
966     } 
967 }
968
969 =item check_cookie_auth
970
971   ($status, $sessionId) = check_api_auth($cookie, $userflags);
972
973 Given a CGISESSID cookie set during a previous login to Koha, determine
974 if the user has the privileges specified by C<$userflags>.
975
976 C<check_cookie_auth> is meant for authenticating special services
977 such as tools/upload-file.pl that are invoked by other pages that
978 have been authenticated in the usual way.
979
980 Possible return values in C<$status> are:
981
982 =over 4
983
984 =item "ok" -- user authenticated; C<$sessionID> have valid values.
985
986 =item "failed" -- credentials are not correct; C<$sessionid> are undef
987
988 =item "maintenance" -- DB is in maintenance mode; no login possible at the moment
989
990 =item "expired -- session cookie has expired; API user should resubmit userid and password
991
992 =back
993
994 =cut
995
996 sub check_cookie_auth {
997     my $cookie = shift;
998     my $flagsrequired = shift;
999
1000     my $dbh     = C4::Context->dbh;
1001     my $timeout = C4::Context->preference('timeout');
1002     $timeout = 600 unless $timeout;
1003
1004     unless (C4::Context->preference('Version')) {
1005         # database has not been installed yet
1006         return ("maintenance", undef);
1007     }
1008     my $kohaversion=C4::Context::KOHAVERSION;
1009     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
1010     if (C4::Context->preference('Version') < $kohaversion) {
1011         # database in need of version update; assume that
1012         # no API should be called while databsae is in
1013         # this condition.
1014         return ("maintenance", undef);
1015     }
1016
1017     # FIXME -- most of what follows is a copy-and-paste
1018     # of code from checkauth.  There is an obvious need
1019     # for refactoring to separate the various parts of
1020     # the authentication code, but as of 2007-11-23 this
1021     # is deferred so as to not introduce bugs into the
1022     # regular authentication code for Koha 3.0.
1023
1024     # see if we have a valid session cookie already
1025     # however, if a userid parameter is present (i.e., from
1026     # a form submission, assume that any current cookie
1027     # is to be ignored
1028     unless (defined $cookie and $cookie) {
1029         return ("failed", undef);
1030     }
1031     my $sessionID = $cookie;
1032     my $session = get_session($sessionID);
1033     C4::Context->_new_userenv($sessionID);
1034     if ($session) {
1035         C4::Context::set_userenv(
1036             $session->param('number'),       $session->param('id'),
1037             $session->param('cardnumber'),   $session->param('firstname'),
1038             $session->param('surname'),      $session->param('branch'),
1039             $session->param('branchname'),   $session->param('flags'),
1040             $session->param('emailaddress'), $session->param('branchprinter')
1041         );
1042
1043         my $ip = $session->param('ip');
1044         my $lasttime = $session->param('lasttime');
1045         my $userid = $session->param('id');
1046         if ( $lasttime < time() - $timeout ) {
1047             # time out
1048             $session->delete();
1049             C4::Context->_unset_userenv($sessionID);
1050             $userid    = undef;
1051             $sessionID = undef;
1052             return ("expired", undef);
1053         } elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
1054             # IP address changed
1055             $session->delete();
1056             C4::Context->_unset_userenv($sessionID);
1057             $userid    = undef;
1058             $sessionID = undef;
1059             return ("expired", undef);
1060         } else {
1061             $session->param('lasttime',time());
1062             my $flags = haspermission( $dbh, $userid, $flagsrequired );
1063             if ($flags) {
1064                 return ("ok", $sessionID);
1065             } else {
1066                 $session->delete();
1067                 C4::Context->_unset_userenv($sessionID);
1068                 $userid    = undef;
1069                 $sessionID = undef;
1070                 return ("failed", undef);
1071             }
1072         }
1073     } else {
1074         return ("expired", undef);
1075     }
1076 }
1077
1078 =item get_session
1079
1080   use CGI::Session;
1081   my $session = get_session($sessionID);
1082
1083 Given a session ID, retrieve the CGI::Session object used to store
1084 the session's state.  The session object can be used to store 
1085 data that needs to be accessed by different scripts during a
1086 user's session.
1087
1088 If the C<$sessionID> parameter is an empty string, a new session
1089 will be created.
1090
1091 =cut
1092
1093 sub get_session {
1094     my $sessionID = shift;
1095     my $storage_method = C4::Context->preference('SessionStorage');
1096     my $dbh = C4::Context->dbh;
1097     my $session;
1098     if ($storage_method eq 'mysql'){
1099         $session = new CGI::Session("driver:MySQL;serializer:yaml", $sessionID, {Handle=>$dbh});
1100     }
1101     elsif ($storage_method eq 'Pg') {
1102         $session = new CGI::Session("driver:PostgreSQL;serializer:yaml", $sessionID, {Handle=>$dbh});
1103     }
1104     else {
1105         # catch all defaults to tmp should work on all systems
1106         $session = new CGI::Session("driver:File;serializer:yaml", $sessionID, {Directory=>'/tmp'});
1107     }
1108     return $session;
1109 }
1110
1111 sub checkpw {
1112
1113     my ( $dbh, $userid, $password ) = @_;
1114     if ($ldap) {
1115         $debug and print "## checkpw - checking LDAP\n";
1116         my ($retval,$retcard) = checkpw_ldap(@_);    # EXTERNAL AUTH
1117         ($retval) and return ($retval,$retcard);
1118     }
1119
1120     # INTERNAL AUTH
1121     my $sth =
1122       $dbh->prepare(
1123 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
1124       );
1125     $sth->execute($userid);
1126     if ( $sth->rows ) {
1127         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1128             $surname, $branchcode, $flags )
1129           = $sth->fetchrow;
1130         if ( md5_base64($password) eq $md5password ) {
1131
1132             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
1133                 $firstname, $surname, $branchcode, $flags );
1134             return 1, $cardnumber;
1135         }
1136     }
1137     $sth =
1138       $dbh->prepare(
1139 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
1140       );
1141     $sth->execute($userid);
1142     if ( $sth->rows ) {
1143         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
1144             $surname, $branchcode, $flags )
1145           = $sth->fetchrow;
1146         if ( md5_base64($password) eq $md5password ) {
1147
1148             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
1149                 $firstname, $surname, $branchcode, $flags );
1150             return 1, $userid;
1151         }
1152     }
1153     if (   $userid && $userid eq C4::Context->config('user')
1154         && "$password" eq C4::Context->config('pass') )
1155     {
1156
1157 # Koha superuser account
1158 #     C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
1159         return 2;
1160     }
1161     if (   $userid && $userid eq 'demo'
1162         && "$password" eq 'demo'
1163         && C4::Context->config('demo') )
1164     {
1165
1166 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
1167 # some features won't be effective : modify systempref, modify MARC structure,
1168         return 2;
1169     }
1170     return 0;
1171 }
1172
1173 =item getuserflags
1174
1175  $authflags = getuserflags($flags,$dbh);
1176 Translates integer flags into permissions strings hash.
1177
1178 C<$flags> is the integer userflags value ( borrowers.userflags )
1179 C<$authflags> is a hashref of permissions
1180
1181 =cut
1182
1183 sub getuserflags {
1184     my $flags   = shift;
1185     my $dbh     = shift;
1186     my $userflags;
1187     $flags = 0 unless $flags;
1188     my $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
1189     $sth->execute;
1190
1191     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
1192         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
1193             $userflags->{$flag} = 1;
1194         }
1195         else {
1196             $userflags->{$flag} = 0;
1197         }
1198     }
1199     return $userflags;
1200 }
1201
1202 =item haspermission 
1203
1204   $flags = ($dbh,$member,$flagsrequired);
1205
1206 C<$member> may be either userid or overloaded with $borrower hashref from GetMemberDetails.
1207 C<$flags> is a hashref of required flags lik C<$borrower-&lt;{authflags}> 
1208
1209 Returns member's flags or 0 if a permission is not met.
1210
1211 =cut
1212
1213 sub haspermission {
1214     my ( $dbh, $userid, $flagsrequired ) = @_;
1215     my ($flags,$intflags);
1216     $dbh=C4::Context->dbh unless($dbh);
1217     if(ref($userid)) {
1218         $intflags = $userid->{'flags'};  
1219     } else {
1220         my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE userid=?");
1221         $sth->execute($userid);
1222         my ($intflags) = $sth->fetchrow;
1223         $flags = getuserflags( $intflags, $dbh );
1224     }
1225     if ( $userid eq C4::Context->config('user') ) {
1226         # Super User Account from /etc/koha.conf
1227         $flags->{'superlibrarian'} = 1;
1228     }
1229     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
1230         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
1231         $flags->{'superlibrarian'} = 1;
1232     }
1233     return $flags if $flags->{superlibrarian};
1234     foreach ( keys %$flagsrequired ) {
1235         return 0 unless( $flags->{$_} );
1236     }
1237     return $flags;
1238     #FIXME - This fcn should return the failed permission so a suitable error msg can be delivered.
1239 }
1240
1241
1242 sub getborrowernumber {
1243     my ($userid) = @_;
1244     my $dbh = C4::Context->dbh;
1245     for my $field ( 'userid', 'cardnumber' ) {
1246         my $sth =
1247           $dbh->prepare("select borrowernumber from borrowers where $field=?");
1248         $sth->execute($userid);
1249         if ( $sth->rows ) {
1250             my ($bnumber) = $sth->fetchrow;
1251             return $bnumber;
1252         }
1253     }
1254     return 0;
1255 }
1256
1257 END { }    # module clean-up code here (global destructor)
1258 1;
1259 __END__
1260
1261 =back
1262
1263 =head1 SEE ALSO
1264
1265 CGI(3)
1266
1267 C4::Output(3)
1268
1269 Digest::MD5(3)
1270
1271 =cut