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