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