Bug #1462 correction.
[koha.git] / C4 / Auth.pm
1 # -*- tab-width: 8 -*-
2 # NOTE: This file uses 8-character tabs; do not change the tab size!
3
4 package C4::Auth;
5
6 # Copyright 2000-2002 Katipo Communications
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it under the
11 # terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 2 of the License, or (at your option) any later
13 # version.
14 #
15 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
16 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License along with
20 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21 # Suite 330, Boston, MA  02111-1307 USA
22
23 use strict;
24 use Digest::MD5 qw(md5_base64);
25 use CGI::Session;
26
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
35 # use utf8;
36 # use Net::LDAP;
37 # use Net::LDAP qw(:all);
38
39 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
40
41 # set the version for version checking
42 $VERSION = do { my @v = '$Revision$' =~ /\d+/g;
43     shift(@v) . "." . join( "_", map { sprintf "%03d", $_ } @v );
44 };
45
46 =head1 NAME
47
48 C4::Auth - Authenticates Koha users
49
50 =head1 SYNOPSIS
51
52   use CGI;
53   use C4::Auth;
54
55   my $query = new CGI;
56
57   my ($template, $borrowernumber, $cookie) 
58     = get_template_and_user(
59         {
60             template_name   => "opac-main.tmpl",
61             query           => $query,
62       type            => "opac",
63       authnotrequired => 1,
64       flagsrequired   => {borrow => 1},
65   }
66     );
67
68   print $query->header(
69     -type => 'utf-8',
70     -cookie => $cookie
71   ), $template->output;
72
73
74 =head1 DESCRIPTION
75
76     The main function of this module is to provide
77     authentification. However the get_template_and_user function has
78     been provided so that a users login information is passed along
79     automatically. This gets loaded into the template.
80
81 =head1 FUNCTIONS
82
83 =over 2
84
85 =cut
86
87 @ISA    = qw(Exporter);
88 @EXPORT = qw(
89   &checkauth
90   &get_template_and_user
91 );
92
93 =item get_template_and_user
94
95   my ($template, $borrowernumber, $cookie)
96     = get_template_and_user(
97         {
98            template_name   => "opac-main.tmpl",
99            query           => $query,
100      type            => "opac",
101      authnotrequired => 1,
102      flagsrequired   => {borrow => 1},
103   }
104     );
105
106     This call passes the C<query>, C<flagsrequired> and C<authnotrequired>
107     to C<&checkauth> (in this module) to perform authentification.
108     See C<&checkauth> for an explanation of these parameters.
109
110     The C<template_name> is then used to find the correct template for
111     the page. The authenticated users details are loaded onto the
112     template in the HTML::Template LOOP variable C<USER_INFO>. Also the
113     C<sessionID> is passed to the template. This can be used in templates
114     if cookies are disabled. It needs to be put as and input to every
115     authenticated page.
116
117     More information on the C<gettemplate> sub can be found in the
118     Output.pm module.
119
120 =cut
121
122 sub get_template_and_user {
123     my $in       = shift;
124     my $template =
125       gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
126     my ( $user, $cookie, $sessionID, $flags ) = checkauth(
127         $in->{'query'},
128         $in->{'authnotrequired'},
129         $in->{'flagsrequired'},
130         $in->{'type'}
131     ) unless ($in->{'template_name'}=~/maintenance/);
132
133     my $borrowernumber;
134     my $insecure = C4::Context->preference('insecure');
135     if ($user or $insecure) {
136
137                 # load the template variables for stylesheets and JavaScript
138                 $template->param( css_libs => $in->{'css_libs'} );
139                 $template->param( css_module => $in->{'css_module'} );
140                 $template->param( css_page => $in->{'css_page'} );
141                 $template->param( css_widgets => $in->{'css_widgets'} );
142
143         $template->param( js_libs => $in->{'js_libs'} );
144         $template->param( js_module => $in->{'js_module'} );
145         $template->param( js_page => $in->{'js_page'} );
146         $template->param( js_widgets => $in->{'js_widgets'} );
147
148                 # user info
149         $template->param( loggedinusername => $user );
150         $template->param( sessionID        => $sessionID );
151
152         $borrowernumber = getborrowernumber($user);
153         my ( $borr, $alternativeflags ) =
154           GetMemberDetails( $borrowernumber );
155         my @bordat;
156         $bordat[0] = $borr;
157         $template->param( "USER_INFO" => \@bordat );
158
159         # We are going to use the $flags returned by checkauth
160         # to create the template's parameters that will indicate
161         # which menus the user can access.
162         if (( $flags && $flags->{superlibrarian}==1) or $insecure==1) {
163             $template->param( CAN_user_circulate        => 1 );
164             $template->param( CAN_user_catalogue        => 1 );
165             $template->param( CAN_user_parameters       => 1 );
166             $template->param( CAN_user_borrowers        => 1 );
167             $template->param( CAN_user_permission       => 1 );
168             $template->param( CAN_user_reserveforothers => 1 );
169             $template->param( CAN_user_borrow           => 1 );
170             $template->param( CAN_user_editcatalogue    => 1 );
171             $template->param( CAN_user_updatecharge     => 1 );
172             $template->param( CAN_user_acquisition      => 1 );
173             $template->param( CAN_user_management       => 1 );
174             $template->param( CAN_user_tools            => 1 ); 
175             $template->param( CAN_user_editauthorities  => 1 );
176             $template->param( CAN_user_serials          => 1 );
177             $template->param( CAN_user_reports          => 1 );
178         }
179
180         if ( $flags && $flags->{circulate} == 1 ) {
181             $template->param( CAN_user_circulate => 1 );
182         }
183
184         if ( $flags && $flags->{catalogue} == 1 ) {
185             $template->param( CAN_user_catalogue => 1 );
186         }
187
188         if ( $flags && $flags->{parameters} == 1 ) {
189             $template->param( CAN_user_parameters => 1 );
190             $template->param( CAN_user_management => 1 );
191         }
192
193         if ( $flags && $flags->{borrowers} == 1 ) {
194             $template->param( CAN_user_borrowers => 1 );
195         }
196
197         if ( $flags && $flags->{permissions} == 1 ) {
198             $template->param( CAN_user_permission => 1 );
199         }
200
201         if ( $flags && $flags->{reserveforothers} == 1 ) {
202             $template->param( CAN_user_reserveforothers => 1 );
203         }
204
205         if ( $flags && $flags->{borrow} == 1 ) {
206             $template->param( CAN_user_borrow => 1 );
207         }
208
209         if ( $flags && $flags->{editcatalogue} == 1 ) {
210             $template->param( CAN_user_editcatalogue => 1 );
211         }
212
213         if ( $flags && $flags->{updatecharges} == 1 ) {
214             $template->param( CAN_user_updatecharge => 1 );
215         }
216
217         if ( $flags && $flags->{acquisition} == 1 ) {
218             $template->param( CAN_user_acquisition => 1 );
219         }
220
221         if ( $flags && $flags->{tools} == 1 ) {
222             $template->param( CAN_user_tools => 1 );
223         }
224   
225         if ( $flags && $flags->{editauthorities} == 1 ) {
226             $template->param( CAN_user_editauthorities => 1 );
227         }
228     
229         if ( $flags && $flags->{serials} == 1 ) {
230             $template->param( CAN_user_serials => 1 );
231         }
232
233         if ( $flags && $flags->{reports} == 1 ) {
234             $template->param( CAN_user_reports => 1 );
235         }
236     }
237     if ( $in->{'type'} eq "intranet" ) {
238         $template->param(
239             intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"),
240             intranetstylesheet => C4::Context->preference("intranetstylesheet"),
241             IntranetNav        => C4::Context->preference("IntranetNav"),
242             intranetuserjs     => C4::Context->preference("intranetuserjs"),
243             TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
244             AmazonContent      => C4::Context->preference("AmazonContent"),
245             LibraryName        => C4::Context->preference("LibraryName"),
246             LoginBranchcode    => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
247             LoginBranchname    => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),
248             LoginFirstname     => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
249             LoginSurname       => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
250             AutoLocation       => C4::Context->preference("AutoLocation"),
251             hide_marc          => C4::Context->preference("hide_marc"),
252             patronimages       => C4::Context->preference("patronimages"),
253             "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
254             advancedMARCEditor      => C4::Context->preference("advancedMARCEditor"),
255             suggestion              => C4::Context->preference("suggestion"),
256             virtualshelves          => C4::Context->preference("virtualshelves"),
257             LibraryName             => C4::Context->preference("LibraryName"),
258             KohaAdminEmailAddress   => "" . C4::Context->preference("KohaAdminEmailAddress"),
259                         IntranetmainUserblock   => C4::Context->preference("IntranetmainUserblock"),
260         );
261     }
262     else {
263         warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]"
264           unless ( $in->{'type'} eq 'opac' );
265         my $LibraryNameTitle = C4::Context->preference("LibraryName");
266         $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
267         $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
268   $template->param(
269             KohaAdminEmailAddress  => "" . C4::Context->preference("KohaAdminEmailAddress"),
270             suggestion             => "" . C4::Context->preference("suggestion"),
271             virtualshelves         => "" . C4::Context->preference("virtualshelves"),
272             OpacNav                => "" . C4::Context->preference("OpacNav"),
273             opacheader             => "" . C4::Context->preference("opacheader"),
274             opaccredits            => "" . C4::Context->preference("opaccredits"),
275             opacsmallimage         => "" . C4::Context->preference("opacsmallimage"),
276             opaclargeimage         => "" . C4::Context->preference("opaclargeimage"),
277             opaclayoutstylesheet   => "". C4::Context->preference("opaclayoutstylesheet"),
278             opaccolorstylesheet    => "". C4::Context->preference("opaccolorstylesheet"),
279             opaclanguagesdisplay   => "". C4::Context->preference("opaclanguagesdisplay"),
280             opacuserlogin          => "" . C4::Context->preference("opacuserlogin"),
281             opacbookbag            => "" . C4::Context->preference("opacbookbag"),
282             TemplateEncoding       => "". C4::Context->preference("TemplateEncoding"),
283             AmazonContent          => "" . C4::Context->preference("AmazonContent"),
284             LibraryName            => "" . C4::Context->preference("LibraryName"),
285             LibraryNameTitle       => "" . $LibraryNameTitle,
286             LoginBranchcode        => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
287             LoginBranchname        => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", 
288             LoginFirstname        => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
289             LoginSurname        => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
290             OpacPasswordChange     => C4::Context->preference("OpacPasswordChange"),
291             opacreadinghistory     => C4::Context->preference("opacreadinghistory"),
292             opacuserjs             => C4::Context->preference("opacuserjs"),
293             OpacCloud              => C4::Context->preference("OpacCloud"),
294             OpacTopissue           => C4::Context->preference("OpacTopissue"),
295             OpacAuthorities        => C4::Context->preference("OpacAuthorities"),
296             OpacBrowser            => C4::Context->preference("OpacBrowser"),
297             RequestOnOpac          => C4::Context->preference("RequestOnOpac"),
298             reviewson              => C4::Context->preference("reviewson"),
299             hide_marc              => C4::Context->preference("hide_marc"),
300             patronimages           => C4::Context->preference("patronimages"),
301             "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
302         );
303     }
304     return ( $template, $borrowernumber, $cookie );
305 }
306
307 =item checkauth
308
309   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
310
311 Verifies that the user is authorized to run this script.  If
312 the user is authorized, a (userid, cookie, session-id, flags)
313 quadruple is returned.  If the user is not authorized but does
314 not have the required privilege (see $flagsrequired below), it
315 displays an error page and exits.  Otherwise, it displays the
316 login page and exits.
317
318 Note that C<&checkauth> will return if and only if the user
319 is authorized, so it should be called early on, before any
320 unfinished operations (e.g., if you've opened a file, then
321 C<&checkauth> won't close it for you).
322
323 C<$query> is the CGI object for the script calling C<&checkauth>.
324
325 The C<$noauth> argument is optional. If it is set, then no
326 authorization is required for the script.
327
328 C<&checkauth> fetches user and session information from C<$query> and
329 ensures that the user is authorized to run scripts that require
330 authorization.
331
332 The C<$flagsrequired> argument specifies the required privileges
333 the user must have if the username and password are correct.
334 It should be specified as a reference-to-hash; keys in the hash
335 should be the "flags" for the user, as specified in the Members
336 intranet module. Any key specified must correspond to a "flag"
337 in the userflags table. E.g., { circulate => 1 } would specify
338 that the user must have the "circulate" privilege in order to
339 proceed. To make sure that access control is correct, the
340 C<$flagsrequired> parameter must be specified correctly.
341
342 The C<$type> argument specifies whether the template should be
343 retrieved from the opac or intranet directory tree.  "opac" is
344 assumed if it is not specified; however, if C<$type> is specified,
345 "intranet" is assumed if it is not "opac".
346
347 If C<$query> does not have a valid session ID associated with it
348 (i.e., the user has not logged in) or if the session has expired,
349 C<&checkauth> presents the user with a login page (from the point of
350 view of the original script, C<&checkauth> does not return). Once the
351 user has authenticated, C<&checkauth> restarts the original script
352 (this time, C<&checkauth> returns).
353
354 The login page is provided using a HTML::Template, which is set in the
355 systempreferences table or at the top of this file. The variable C<$type>
356 selects which template to use, either the opac or the intranet 
357 authentification template.
358
359 C<&checkauth> returns a user ID, a cookie, and a session ID. The
360 cookie should be sent back to the browser; it verifies that the user
361 has authenticated.
362
363 =cut
364
365 sub checkauth {
366     my $query = shift;
367   # warn "Checking Auth";
368     # $authnotrequired will be set for scripts which will run without authentication
369     my $authnotrequired = shift;
370     my $flagsrequired   = shift;
371     my $type            = shift;
372     $type = 'opac' unless $type;
373
374     my $dbh     = C4::Context->dbh;
375     my $timeout = C4::Context->preference('timeout');
376     $timeout = 600 unless $timeout;
377
378
379     # If Version syspref is unavailable, it means Koha is beeing installed,
380     # and so we must redirect to OPAC maintenance page or to the WebInstaller
381     warn "about to check version";
382     unless (C4::Context->preference('Version')) {
383       if ($type ne 'opac') {
384         warn "Install required, redirecting to Installer";
385         print $query->redirect("/cgi-bin/koha/installer/install.pl");
386       } 
387       else {
388         warn "OPAC Install required, redirecting to maintenance";
389         print $query->redirect("/cgi-bin/koha/maintenance.pl");
390       }
391       exit;
392     }
393
394
395     # state variables
396     my $loggedin = 0;
397     my %info;
398     my ( $userid, $cookie, $sessionID, $flags );
399     my $logout = $query->param('logout.x');
400     if ( $userid = $ENV{'REMOTE_USER'} ) {
401         # Using Basic Authentication, no cookies required
402         $cookie = $query->cookie(
403             -name    => 'CGISESSID',
404             -value   => '',
405             -expires => ''
406         );
407         $loggedin = 1;
408     }
409     elsif ( $sessionID = $query->cookie("CGISESSID")) {
410 #         my $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
411         my $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});
412         C4::Context->_new_userenv($sessionID);
413         if ($session){
414             C4::Context::set_userenv(
415                 $session->param('number'),       $session->param('id'),
416                 $session->param('cardnumber'),   $session->param('firstname'),
417                 $session->param('surname'),      $session->param('branch'),
418                 $session->param('branchname'),   $session->param('flags'),
419                 $session->param('emailaddress'), $session->param('branchprinter')
420             );
421 #             warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
422 #                 ".$session->param('surname').",      ".$session->param('branch');
423         }
424         my $ip;
425         my $lasttime;
426         if ($session) {
427           $ip = $session->param('ip');
428           $lasttime = $session->param('lasttime');
429                 $userid = $session->param('id');
430         }
431         
432     
433         if ($logout) {
434
435             # voluntary logout the user
436 #             $session->delete;
437             $session->flush;      
438             C4::Context->_unset_userenv($sessionID);
439             $sessionID = undef;
440             $userid    = undef;
441             open L, ">>/tmp/sessionlog";
442             my $time = localtime( time() );
443             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
444               $ip, $time;
445             close L;
446         }
447         if ($userid) {
448             if ( $lasttime < time() - $timeout ) {
449                 # timed logout
450                 $info{'timed_out'} = 1;
451                 $session->delete();
452                 C4::Context->_unset_userenv($sessionID);
453                 $userid    = undef;
454                 $sessionID = undef;
455                 open L, ">>/tmp/sessionlog";
456                 my $time = localtime( time() );
457                 printf L "%20s from %16s logged out at %30s (inactivity).\n",
458                   $userid, $ip, $time;
459                 close L;
460             }
461             elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
462                 # Different ip than originally logged in from
463                 $info{'oldip'}        = $ip;
464                 $info{'newip'}        = $ENV{'REMOTE_ADDR'};
465                 $info{'different_ip'} = 1;
466         $session->delete();
467                 C4::Context->_unset_userenv($sessionID);
468                 $sessionID = undef;
469                 $userid    = undef;
470                 open L, ">>/tmp/sessionlog";
471                 my $time = localtime( time() );
472                 printf L
473 "%20s from logged out at %30s (ip changed from %16s to %16s).\n",
474                   $userid, $time, $ip, $info{'newip'};
475                 close L;
476             }
477             else {
478                 $cookie = $query->cookie( CGISESSID => $session->id );
479                 $session->param('lasttime',time());
480                 $flags = haspermission( $dbh, $userid, $flagsrequired );
481                 if ($flags) {
482                     $loggedin = 1;
483                 }
484                 else {
485                     $info{'nopermission'} = 1;
486                 }
487             }
488         }
489     }
490     unless ($userid) {
491 #         my $session = new CGI::Session("driver:MySQL", undef, {Handle=>$dbh});    
492         my $session = new CGI::Session("driver:File", undef, {Directory=>'/tmp'});    
493         my $sessionID;
494     if ($session) {
495       $sessionID = $session->id;
496       }
497         $userid    = $query->param('userid');
498         C4::Context->_new_userenv($sessionID);
499         my $password = $query->param('password');
500         C4::Context->_new_userenv($sessionID);
501         my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
502         if ($return) {
503             open L, ">>/tmp/sessionlog";
504             my $time = localtime( time() );
505             printf L "%20s from %16s logged in  at %30s.\n", $userid,
506               $ENV{'REMOTE_ADDR'}, $time;
507             close L;
508             $cookie = $query->cookie(CGISESSID => $sessionID);
509             if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
510                 $loggedin = 1;
511             }
512             else {
513                 $info{'nopermission'} = 1;
514                 C4::Context->_unset_userenv($sessionID);
515             }
516             if ( $return == 1 ) {
517                 my (
518                     $borrowernumber, $firstname,  $surname,
519                     $userflags,      $branchcode, $branchname,
520                     $branchprinter,  $emailaddress
521                 );
522                 my $sth =
523                   $dbh->prepare(
524 "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=?"
525                   );
526                 $sth->execute($userid);
527                 (
528                     $borrowernumber, $firstname,  $surname,
529                     $userflags,      $branchcode, $branchname,
530                     $branchprinter,  $emailaddress
531                   )
532                   = $sth->fetchrow
533                   if ( $sth->rows );
534
535 #         warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
536                 unless ( $sth->rows ) {
537                     my $sth =
538                       $dbh->prepare(
539 "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=?"
540                       );
541                     $sth->execute($cardnumber);
542                     (
543                         $borrowernumber, $firstname,  $surname,
544                         $userflags,      $branchcode, $branchname,
545                         $branchprinter,  $emailaddress
546                       )
547                       = $sth->fetchrow
548                       if ( $sth->rows );
549
550 #           warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
551                     unless ( $sth->rows ) {
552                         $sth->execute($userid);
553                         (
554                             $borrowernumber, $firstname, $surname, $userflags,
555                             $branchcode, $branchname, $branchprinter, $emailaddress
556                           )
557                           = $sth->fetchrow
558                           if ( $sth->rows );
559                     }
560                 }
561
562 # launch a sequence to check if we have a ip for the branch, if we have one we replace the branchcode of the userenv by the branch bound in the ip.
563                 my $ip       = $ENV{'REMOTE_ADDR'};
564                 # if they specify at login, use that
565                 if ($query->param('branch')) {
566                     $branchcode  = $query->param('branch');
567                     $branchname = GetBranchName($branchcode);
568                 }
569                 my $branches = GetBranches();
570                 my @branchesloop;
571                 foreach my $br ( keys %$branches ) {
572                     #     now we work with the treatment of ip
573                     my $domain = $branches->{$br}->{'branchip'};
574                     if ( $domain && $ip =~ /^$domain/ ) {
575                         $branchcode = $branches->{$br}->{'branchcode'};
576
577                         # new op dev : add the branchprinter and branchname in the cookie
578                         $branchprinter = $branches->{$br}->{'branchprinter'};
579                         $branchname    = $branches->{$br}->{'branchname'};
580                     }
581                 }
582                 $session->param('number',$borrowernumber);
583                 $session->param('id',$userid);
584                 $session->param('cardnumber',$cardnumber);
585                 $session->param('firstname',$firstname);
586                 $session->param('surname',$surname);
587                 $session->param('branch',$branchcode);
588                 $session->param('branchname',$branchname);
589                 $session->param('flags',$userflags);
590                 $session->param('emailaddress',$emailaddress);
591                 $session->param('ip',$session->remote_addr());
592                 $session->param('lasttime',time());
593 #            warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
594 #                 ".$session->param('surname').",      ".$session->param('branch');
595             }
596             elsif ( $return == 2 ) {
597                 #We suppose the user is the superlibrarian
598                         $session->param('number',0);
599                         $session->param('id',C4::Context->config('user'));
600                         $session->param('cardnumber',C4::Context->config('user'));
601                         $session->param('firstname',C4::Context->config('user'));
602                         $session->param('surname',C4::Context->config('user'));
603                         $session->param('branch','NO_LIBRARY_SET');
604                         $session->param('branchname','NO_LIBRARY_SET');
605                         $session->param('flags',1);
606                         $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
607                         $session->param('ip',$session->remote_addr());
608                         $session->param('lasttime',time());
609                 }
610                 if ($session){
611                     C4::Context::set_userenv(
612                         $session->param('number'),       $session->param('id'),
613                         $session->param('cardnumber'),   $session->param('firstname'),
614                         $session->param('surname'),      $session->param('branch'),
615                         $session->param('branchname'),   $session->param('flags'),
616                         $session->param('emailaddress'), $session->param('branchprinter')
617                     );
618                 }
619         }
620
621         else {
622             if ($userid) {
623                 $info{'invalid_username_or_password'} = 1;
624                 C4::Context->_unset_userenv($sessionID);
625             }
626         }
627     }
628     my $insecure = C4::Context->boolean_preference('insecure');
629
630     # finished authentification, now respond
631     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
632     {
633         # successful login
634         unless ($cookie) {
635             $cookie = $query->cookie( CGISESSID => ''
636             );
637         }
638     return ( $userid, $cookie, $sessionID, $flags );
639
640     }
641
642 #
643 #
644 # AUTH rejected, show the login/password template, after checking the DB.
645 #
646 #
647     
648     # get the inputs from the incoming query
649     my @inputs = ();
650     foreach my $name ( param $query) {
651         (next) if ( $name eq 'userid' || $name eq 'password' );
652         my $value = $query->param($name);
653         push @inputs, { name => $name, value => $value };
654     }
655     # get the branchloop, which we need for authentication
656     my $branches = GetBranches();
657     my @branch_loop;
658     for my $branch_hash (keys %$branches) {
659                 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
660     }
661
662     # check that database and koha version are the same
663     # there is no DB version, it's a fresh install,
664     # go to web installer
665     # there is a DB version, compare it to the code version
666     my $kohaversion=C4::Context::KOHAVERSION;
667     # remove the 3 last . to have a Perl number
668     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
669 #     warn "kohaversion : $kohaversion";
670     if (C4::Context->preference('Version') < $kohaversion){
671       if ($type ne 'opac'){
672       warn "Database update needed, redirecting to Installer. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
673         print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
674       } else {
675       warn "OPAC :Database update needed, redirecting to maintenance. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
676         print $query->redirect("/cgi-bin/koha/maintenance.pl");
677       }       
678       exit;
679     }
680     my $template_name;
681     if ( $type eq 'opac' ) {
682         $template_name = "opac-auth.tmpl";
683     }
684     else {
685         $template_name = "auth.tmpl";
686     }
687     my $template = gettemplate( $template_name, $type, $query );
688     $template->param(branchloop => \@branch_loop,);
689     $template->param(
690     login        => 1,
691         INPUTS               => \@inputs,
692         suggestion           => C4::Context->preference("suggestion"),
693         virtualshelves       => C4::Context->preference("virtualshelves"),
694         opaclargeimage       => C4::Context->preference("opaclargeimage"),
695         LibraryName          => C4::Context->preference("LibraryName"),
696         OpacNav              => C4::Context->preference("OpacNav"),
697         opaccredits          => C4::Context->preference("opaccredits"),
698         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
699         opacsmallimage       => C4::Context->preference("opacsmallimage"),
700         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
701         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
702         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
703         opacuserjs           => C4::Context->preference("opacuserjs"),
704
705         intranetcolorstylesheet =>
706           C4::Context->preference("intranetcolorstylesheet"),
707         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
708         IntranetNav        => C4::Context->preference("IntranetNav"),
709         intranetuserjs     => C4::Context->preference("intranetuserjs"),
710         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
711
712     );
713     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
714
715     my $self_url = $query->url( -absolute => 1 );
716     $template->param(
717         url         => $self_url,
718         LibraryName => => C4::Context->preference("LibraryName"),
719     );
720     $template->param( \%info );
721 #    $cookie = $query->cookie(CGISESSID => $session->id
722 #   );
723     print $query->header(
724                 -type   => 'text/html',
725         -charset => 'utf-8',
726         -cookie => $cookie
727       ),
728       $template->output;
729     exit;
730 }
731
732 sub checkpw {
733
734     my ( $dbh, $userid, $password ) = @_;
735
736     # INTERNAL AUTH
737     my $sth =
738       $dbh->prepare(
739 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
740       );
741     $sth->execute($userid);
742     if ( $sth->rows ) {
743         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
744             $surname, $branchcode, $flags )
745           = $sth->fetchrow;
746         if ( md5_base64($password) eq $md5password ) {
747
748             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
749                 $firstname, $surname, $branchcode, $flags );
750             return 1, $cardnumber;
751         }
752     }
753     $sth =
754       $dbh->prepare(
755 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
756       );
757     $sth->execute($userid);
758     if ( $sth->rows ) {
759         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
760             $surname, $branchcode, $flags )
761           = $sth->fetchrow;
762         if ( md5_base64($password) eq $md5password ) {
763
764             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
765                 $firstname, $surname, $branchcode, $flags );
766             return 1, $userid;
767         }
768     }
769     if (   $userid && $userid eq C4::Context->config('user')
770         && "$password" eq C4::Context->config('pass') )
771     {
772
773 # Koha superuser account
774 #     C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
775         return 2;
776     }
777     if (   $userid && $userid eq 'demo'
778         && "$password" eq 'demo'
779         && C4::Context->config('demo') )
780     {
781
782 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
783 # some features won't be effective : modify systempref, modify MARC structure,
784         return 2;
785     }
786     return 0;
787 }
788
789 sub getuserflags {
790     my $cardnumber = shift;
791     my $dbh        = shift;
792     my $userflags;
793     my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE cardnumber=?");
794     $sth->execute($cardnumber);
795     my ($flags) = $sth->fetchrow;
796     $flags = 0 unless $flags;
797     $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
798     $sth->execute;
799
800     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
801         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
802             $userflags->{$flag} = 1;
803         }
804         else {
805             $userflags->{$flag} = 0;
806         }
807     }
808     return $userflags;
809 }
810
811 sub haspermission {
812     my ( $dbh, $userid, $flagsrequired ) = @_;
813     my $sth = $dbh->prepare("SELECT cardnumber FROM borrowers WHERE userid=?");
814     $sth->execute($userid);
815     my ($cardnumber) = $sth->fetchrow;
816     ($cardnumber) || ( $cardnumber = $userid );
817     my $flags = getuserflags( $cardnumber, $dbh );
818     my $configfile;
819     if ( $userid eq C4::Context->config('user') ) {
820
821         # Super User Account from /etc/koha.conf
822         $flags->{'superlibrarian'} = 1;
823     }
824     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
825
826         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
827         $flags->{'superlibrarian'} = 1;
828     }
829     return $flags if $flags->{superlibrarian};
830     foreach ( keys %$flagsrequired ) {
831         return $flags if $flags->{$_};
832     }
833     return 0;
834 }
835
836 sub getborrowernumber {
837     my ($userid) = @_;
838     my $dbh = C4::Context->dbh;
839     for my $field ( 'userid', 'cardnumber' ) {
840         my $sth =
841           $dbh->prepare("select borrowernumber from borrowers where $field=?");
842         $sth->execute($userid);
843         if ( $sth->rows ) {
844             my ($bnumber) = $sth->fetchrow;
845             return $bnumber;
846         }
847     }
848     return 0;
849 }
850
851 END { }    # module clean-up code here (global destructor)
852 1;
853 __END__
854
855 =back
856
857 =head1 SEE ALSO
858
859 CGI(3)
860
861 C4::Output(3)
862
863 Digest::MD5(3)
864
865 =cut