BUGFIX : get available language and theme
[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             }
566             elsif ( $return == 2 ) {
567                 #We suppose the user is the superlibrarian
568                         $session->param('number',0);
569                         $session->param('id',C4::Context->config('user'));
570                         $session->param('cardnumber',C4::Context->config('user'));
571                         $session->param('firstname',C4::Context->config('user'));
572                         $session->param('surname',C4::Context->config('user'),);
573                         $session->param('branch','NO_LIBRARY_SET');
574                         $session->param('branchname','NO_LIBRARY_SET');
575                         $session->param('flags',1);
576                         $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
577                         $session->param('ip',$session->remote_addr());
578                         $session->param('lasttime',time());
579                 }
580                 if ($session){
581                     C4::Context::set_userenv(
582                         $session->param('number'),       $session->param('id'),
583                         $session->param('cardnumber'),   $session->param('firstname'),
584                         $session->param('surname'),      $session->param('branch'),
585                         $session->param('branchname'),   $session->param('flags'),
586                         $session->param('emailaddress'), $session->param('branchprinter')
587                     );
588                 }
589         }
590
591         else {
592             if ($userid) {
593                 $info{'invalid_username_or_password'} = 1;
594                 C4::Context->_unset_userenv($sessionID);
595             }
596         }
597     }
598     my $insecure = C4::Context->boolean_preference('insecure');
599
600     # finished authentification, now respond
601     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
602     {
603         # successful login
604         unless ($cookie) {
605             $cookie = $query->cookie( CGISESSID => ''
606             );
607         }
608                 return ( $userid, $cookie, $sessionID, $flags );
609
610     }
611
612 #
613 #
614 # AUTH rejected, show the login/password template, after checking the DB.
615 #
616 #
617     
618     # get the inputs from the incoming query
619     my @inputs = ();
620     foreach my $name ( param $query) {
621         (next) if ( $name eq 'userid' || $name eq 'password' );
622         my $value = $query->param($name);
623         push @inputs, { name => $name, value => $value };
624     }
625     # get the branchloop, which we need for authentication
626     my $branches = GetBranches();
627     my @branch_loop;
628     for my $branch_hash (keys %$branches) {
629                 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
630     }
631
632     # check that database and koha version are the same
633     # there is no DB version, it's a fresh install,
634     # go to web installer
635         warn "about to check version";
636     unless (C4::Context->preference('Version')){
637       if ($type ne 'opac'){
638         warn "Install required, redirecting to Installer";
639         print $query->redirect("/cgi-bin/koha/installer/install.pl");
640       } else {
641         warn "OPAC Install required, redirecting to maintenance";
642         print $query->redirect("/cgi-bin/koha/maintenance.pl");
643       }       
644       exit;
645     }
646     # there is a DB version, compare it to the code version
647     my $kohaversion=C4::Context::KOHAVERSION;
648     # remove the 3 last . to have a Perl number
649     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
650 #     warn "kohaversion : $kohaversion";
651     if (C4::Context->preference('Version') < $kohaversion){
652       if ($type ne 'opac'){
653       warn "Database update needed, redirecting to Installer. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
654         print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
655       } else {
656       warn "OPAC :Database update needed, redirecting to maintenance. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
657         print $query->redirect("/cgi-bin/koha/maintenance.pl");
658       }       
659       exit;
660     }
661     my $template_name;
662     if ( $type eq 'opac' ) {
663         $template_name = "opac-auth.tmpl";
664     }
665     else {
666         $template_name = "auth.tmpl";
667     }
668     my $template = gettemplate( $template_name, $type, $query );
669     $template->param(branchloop => \@branch_loop,);
670     $template->param(
671                 login                            => 1,
672         INPUTS               => \@inputs,
673         suggestion           => C4::Context->preference("suggestion"),
674         virtualshelves       => C4::Context->preference("virtualshelves"),
675         opaclargeimage       => C4::Context->preference("opaclargeimage"),
676         LibraryName          => C4::Context->preference("LibraryName"),
677         OpacNav              => C4::Context->preference("OpacNav"),
678         opaccredits          => C4::Context->preference("opaccredits"),
679         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
680         opacsmallimage       => C4::Context->preference("opacsmallimage"),
681         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
682         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
683         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
684         opacuserjs           => C4::Context->preference("opacuserjs"),
685
686         intranetcolorstylesheet =>
687           C4::Context->preference("intranetcolorstylesheet"),
688         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
689         IntranetNav        => C4::Context->preference("IntranetNav"),
690         intranetuserjs     => C4::Context->preference("intranetuserjs"),
691         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
692
693     );
694     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
695
696     my $self_url = $query->url( -absolute => 1 );
697     $template->param(
698         url         => $self_url,
699         LibraryName => => C4::Context->preference("LibraryName"),
700     );
701     $template->param( \%info );
702 #    $cookie = $query->cookie(CGISESSID => $session->id
703 #   );
704     print $query->header(
705                 -type   => 'text/html',
706         -charset => 'utf-8',
707         -cookie => $cookie
708       ),
709       $template->output;
710     exit;
711 }
712
713 sub checkpw {
714
715     my ( $dbh, $userid, $password ) = @_;
716
717     # INTERNAL AUTH
718     my $sth =
719       $dbh->prepare(
720 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
721       );
722     $sth->execute($userid);
723     if ( $sth->rows ) {
724         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
725             $surname, $branchcode, $flags )
726           = $sth->fetchrow;
727         if ( md5_base64($password) eq $md5password ) {
728
729             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
730                 $firstname, $surname, $branchcode, $flags );
731             return 1, $cardnumber;
732         }
733     }
734     $sth =
735       $dbh->prepare(
736 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
737       );
738     $sth->execute($userid);
739     if ( $sth->rows ) {
740         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
741             $surname, $branchcode, $flags )
742           = $sth->fetchrow;
743         if ( md5_base64($password) eq $md5password ) {
744
745             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
746                 $firstname, $surname, $branchcode, $flags );
747             return 1, $userid;
748         }
749     }
750     if (   $userid && $userid eq C4::Context->config('user')
751         && "$password" eq C4::Context->config('pass') )
752     {
753
754 # Koha superuser account
755 #               C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
756         return 2;
757     }
758     if (   $userid && $userid eq 'demo'
759         && "$password" eq 'demo'
760         && C4::Context->config('demo') )
761     {
762
763 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
764 # some features won't be effective : modify systempref, modify MARC structure,
765         return 2;
766     }
767     return 0;
768 }
769
770 sub getuserflags {
771     my $cardnumber = shift;
772     my $dbh        = shift;
773     my $userflags;
774     my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE cardnumber=?");
775     $sth->execute($cardnumber);
776     my ($flags) = $sth->fetchrow;
777     $flags = 0 unless $flags;
778     $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
779     $sth->execute;
780
781     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
782         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
783             $userflags->{$flag} = 1;
784         }
785         else {
786             $userflags->{$flag} = 0;
787         }
788     }
789     return $userflags;
790 }
791
792 sub haspermission {
793     my ( $dbh, $userid, $flagsrequired ) = @_;
794     my $sth = $dbh->prepare("SELECT cardnumber FROM borrowers WHERE userid=?");
795     $sth->execute($userid);
796     my ($cardnumber) = $sth->fetchrow;
797     ($cardnumber) || ( $cardnumber = $userid );
798     my $flags = getuserflags( $cardnumber, $dbh );
799     my $configfile;
800     if ( $userid eq C4::Context->config('user') ) {
801
802         # Super User Account from /etc/koha.conf
803         $flags->{'superlibrarian'} = 1;
804     }
805     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
806
807         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
808         $flags->{'superlibrarian'} = 1;
809     }
810     return $flags if $flags->{superlibrarian};
811     foreach ( keys %$flagsrequired ) {
812         return $flags if $flags->{$_};
813     }
814     return 0;
815 }
816
817 sub getborrowernumber {
818     my ($userid) = @_;
819     my $dbh = C4::Context->dbh;
820     for my $field ( 'userid', 'cardnumber' ) {
821         my $sth =
822           $dbh->prepare("select borrowernumber from borrowers where $field=?");
823         $sth->execute($userid);
824         if ( $sth->rows ) {
825             my ($bnumber) = $sth->fetchrow;
826             return $bnumber;
827         }
828     }
829     return 0;
830 }
831
832 END { }    # module clean-up code here (global destructor)
833 1;
834 __END__
835
836 =back
837
838 =head1 SEE ALSO
839
840 CGI(3)
841
842 C4::Output(3)
843
844 Digest::MD5(3)
845
846 =cut