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