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