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