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