Enabling yuipath system preference for choosing local or Yahoo-hosted YUI js library...
[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                         yuipath => C4::Context->preference("yuipath"),
260         );
261     }
262     else {
263         warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
264         my $LibraryNameTitle = C4::Context->preference("LibraryName");
265         $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
266         $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
267   $template->param(
268             KohaAdminEmailAddress  => "" . C4::Context->preference("KohaAdminEmailAddress"),
269             suggestion             => "" . C4::Context->preference("suggestion"),
270             virtualshelves         => "" . C4::Context->preference("virtualshelves"),
271             OpacNav                => "" . C4::Context->preference("OpacNav"),
272             opacheader             => "" . C4::Context->preference("opacheader"),
273             opaccredits            => "" . C4::Context->preference("opaccredits"),
274             opacsmallimage         => "" . C4::Context->preference("opacsmallimage"),
275             opaclargeimage         => "" . C4::Context->preference("opaclargeimage"),
276             opaclayoutstylesheet   => "". C4::Context->preference("opaclayoutstylesheet"),
277             opaccolorstylesheet    => "". C4::Context->preference("opaccolorstylesheet"),
278             opaclanguagesdisplay   => "". C4::Context->preference("opaclanguagesdisplay"),
279             opacuserlogin          => "" . C4::Context->preference("opacuserlogin"),
280             opacbookbag            => "" . C4::Context->preference("opacbookbag"),
281             TemplateEncoding       => "". C4::Context->preference("TemplateEncoding"),
282             AmazonContent          => "" . C4::Context->preference("AmazonContent"),
283             LibraryName            => "" . C4::Context->preference("LibraryName"),
284             LibraryNameTitle       => "" . $LibraryNameTitle,
285             LoginBranchcode        => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
286             LoginBranchname        => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", 
287             LoginFirstname        => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
288             LoginSurname        => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
289             OpacPasswordChange     => C4::Context->preference("OpacPasswordChange"),
290             opacreadinghistory     => C4::Context->preference("opacreadinghistory"),
291             opacuserjs             => C4::Context->preference("opacuserjs"),
292             OpacCloud              => C4::Context->preference("OpacCloud"),
293             OpacTopissue           => C4::Context->preference("OpacTopissue"),
294             OpacAuthorities        => C4::Context->preference("OpacAuthorities"),
295             OpacBrowser            => C4::Context->preference("OpacBrowser"),
296             RequestOnOpac          => C4::Context->preference("RequestOnOpac"),
297             reviewson              => C4::Context->preference("reviewson"),
298             hide_marc              => C4::Context->preference("hide_marc"),
299             patronimages           => C4::Context->preference("patronimages"),
300             mylibraryfirst   => C4::Context->preference("SearchMyLibraryFirst"),
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
379     # If Version syspref is unavailable, it means Koha is beeing installed,
380     # and so we must redirect to OPAC maintenance page or to the WebInstaller
381     #warn "about to check version";
382     unless (C4::Context->preference('Version')) {
383       if ($type ne 'opac') {
384         warn "Install required, redirecting to Installer";
385         print $query->redirect("/cgi-bin/koha/installer/install.pl");
386       } 
387       else {
388         warn "OPAC Install required, redirecting to maintenance";
389         print $query->redirect("/cgi-bin/koha/maintenance.pl");
390       }
391       exit;
392     }
393
394
395     # state variables
396     my $loggedin = 0;
397     my %info;
398     my ( $userid, $cookie, $sessionID, $flags );
399     my $logout = $query->param('logout.x');
400     if ( $userid = $ENV{'REMOTE_USER'} ) {
401         # Using Basic Authentication, no cookies required
402         $cookie = $query->cookie(
403             -name    => 'CGISESSID',
404             -value   => '',
405             -expires => ''
406         );
407         $loggedin = 1;
408     }
409     elsif ( $sessionID = $query->cookie("CGISESSID")) {
410                 my $storage_method = C4::Context->preference('SessionStorage');
411                 my $session;
412                 if ($storage_method eq 'mysql'){
413                     $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
414                 }
415                 else {
416                         # catch all defaults to tmp should work on all systems
417                         $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});                    
418                 }
419         C4::Context->_new_userenv($sessionID);
420         if ($session){
421             C4::Context::set_userenv(
422                 $session->param('number'),       $session->param('id'),
423                 $session->param('cardnumber'),   $session->param('firstname'),
424                 $session->param('surname'),      $session->param('branch'),
425                 $session->param('branchname'),   $session->param('flags'),
426                 $session->param('emailaddress'), $session->param('branchprinter')
427             );
428 #             warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
429 #                 ".$session->param('surname').",      ".$session->param('branch');
430         }
431         my $ip;
432         my $lasttime;
433         if ($session) {
434           $ip = $session->param('ip');
435           $lasttime = $session->param('lasttime');
436                 $userid = $session->param('id');
437         }
438         
439     
440         if ($logout) {
441
442             # voluntary logout the user
443
444             $session->flush;      
445                         $session->delete();
446             C4::Context->_unset_userenv($sessionID);
447             $sessionID = undef;
448             $userid    = undef;
449             open L, ">>/tmp/sessionlog";
450             my $time = localtime( time() );
451             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
452               $ip, $time;
453             close L;
454         }
455         if ($userid) {
456             if ( $lasttime < time() - $timeout ) {
457                 # timed logout
458                 $info{'timed_out'} = 1;
459                 $session->delete();
460                 C4::Context->_unset_userenv($sessionID);
461                 $userid    = undef;
462                 $sessionID = undef;
463                 open L, ">>/tmp/sessionlog";
464                 my $time = localtime( time() );
465                 printf L "%20s from %16s logged out at %30s (inactivity).\n",
466                   $userid, $ip, $time;
467                 close L;
468             }
469             elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
470                 # Different ip than originally logged in from
471                 $info{'oldip'}        = $ip;
472                 $info{'newip'}        = $ENV{'REMOTE_ADDR'};
473                 $info{'different_ip'} = 1;
474         $session->delete();
475                 C4::Context->_unset_userenv($sessionID);
476                 $sessionID = undef;
477                 $userid    = undef;
478                 open L, ">>/tmp/sessionlog";
479                 my $time = localtime( time() );
480                 printf L
481 "%20s from logged out at %30s (ip changed from %16s to %16s).\n",
482                   $userid, $time, $ip, $info{'newip'};
483                 close L;
484             }
485             else {
486                 $cookie = $query->cookie( CGISESSID => $session->id );
487                 $session->param('lasttime',time());
488                 $flags = haspermission( $dbh, $userid, $flagsrequired );
489                 if ($flags) {
490                     $loggedin = 1;
491                 }
492                 else {
493                     $info{'nopermission'} = 1;
494                 }
495             }
496         }
497     }
498     unless ($userid) {
499                 my $storage_method = C4::Context->preference('SessionStorage');
500                 my $session;
501                 if ($storage_method eq 'mysql'){
502                     $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
503                 }
504                 else {
505                         # catch all defaults to tmp should work on all systems
506                         $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});                    
507                 }
508
509         my $sessionID;
510                 if ($session) {
511                         $sessionID = $session->id;
512                 }
513         $userid    = $query->param('userid');
514         C4::Context->_new_userenv($sessionID);
515         my $password = $query->param('password');
516         C4::Context->_new_userenv($sessionID);
517         my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
518         if ($return) {
519             open L, ">>/tmp/sessionlog";
520             my $time = localtime( time() );
521             printf L "%20s from %16s logged in  at %30s.\n", $userid,
522               $ENV{'REMOTE_ADDR'}, $time;
523             close L;
524             $cookie = $query->cookie(CGISESSID => $sessionID);
525             if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
526                 $loggedin = 1;
527             }
528             else {
529                 $info{'nopermission'} = 1;
530                 C4::Context->_unset_userenv($sessionID);
531             }
532             if ( $return == 1 ) {
533                 my (
534                     $borrowernumber, $firstname,  $surname,
535                     $userflags,      $branchcode, $branchname,
536                     $branchprinter,  $emailaddress
537                 );
538                 my $sth =
539                   $dbh->prepare(
540 "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=?"
541                   );
542                 $sth->execute($userid);
543                 (
544                     $borrowernumber, $firstname,  $surname,
545                     $userflags,      $branchcode, $branchname,
546                     $branchprinter,  $emailaddress
547                   )
548                   = $sth->fetchrow
549                   if ( $sth->rows );
550
551 #         warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
552                 unless ( $sth->rows ) {
553                     my $sth =
554                       $dbh->prepare(
555 "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=?"
556                       );
557                     $sth->execute($cardnumber);
558                     (
559                         $borrowernumber, $firstname,  $surname,
560                         $userflags,      $branchcode, $branchname,
561                         $branchprinter,  $emailaddress
562                       )
563                       = $sth->fetchrow
564                       if ( $sth->rows );
565
566 #           warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
567                     unless ( $sth->rows ) {
568                         $sth->execute($userid);
569                         (
570                             $borrowernumber, $firstname, $surname, $userflags,
571                             $branchcode, $branchname, $branchprinter, $emailaddress
572                           )
573                           = $sth->fetchrow
574                           if ( $sth->rows );
575                     }
576                 }
577
578 # 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.
579                 my $ip       = $ENV{'REMOTE_ADDR'};
580                 # if they specify at login, use that
581                 if ($query->param('branch')) {
582                     $branchcode  = $query->param('branch');
583                     $branchname = GetBranchName($branchcode);
584                 }
585                 my $branches = GetBranches();
586                 my @branchesloop;
587                 foreach my $br ( keys %$branches ) {
588                     #     now we work with the treatment of ip
589                     my $domain = $branches->{$br}->{'branchip'};
590                     if ( $domain && $ip =~ /^$domain/ ) {
591                         $branchcode = $branches->{$br}->{'branchcode'};
592
593                         # new op dev : add the branchprinter and branchname in the cookie
594                         $branchprinter = $branches->{$br}->{'branchprinter'};
595                         $branchname    = $branches->{$br}->{'branchname'};
596                     }
597                 }
598                 $session->param('number',$borrowernumber);
599                 $session->param('id',$userid);
600                 $session->param('cardnumber',$cardnumber);
601                 $session->param('firstname',$firstname);
602                 $session->param('surname',$surname);
603                 $session->param('branch',$branchcode);
604                 $session->param('branchname',$branchname);
605                 $session->param('flags',$userflags);
606                 $session->param('emailaddress',$emailaddress);
607                 $session->param('ip',$session->remote_addr());
608                 $session->param('lasttime',time());
609 #            warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
610 #                 ".$session->param('surname').",      ".$session->param('branch');
611             }
612             elsif ( $return == 2 ) {
613                 #We suppose the user is the superlibrarian
614                         $session->param('number',0);
615                         $session->param('id',C4::Context->config('user'));
616                         $session->param('cardnumber',C4::Context->config('user'));
617                         $session->param('firstname',C4::Context->config('user'));
618                         $session->param('surname',C4::Context->config('user'));
619                         $session->param('branch','NO_LIBRARY_SET');
620                         $session->param('branchname','NO_LIBRARY_SET');
621                         $session->param('flags',1);
622                         $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
623                         $session->param('ip',$session->remote_addr());
624                         $session->param('lasttime',time());
625                 }
626                 if ($session){
627                     C4::Context::set_userenv(
628                         $session->param('number'),       $session->param('id'),
629                         $session->param('cardnumber'),   $session->param('firstname'),
630                         $session->param('surname'),      $session->param('branch'),
631                         $session->param('branchname'),   $session->param('flags'),
632                         $session->param('emailaddress'), $session->param('branchprinter')
633                     );
634                 }
635         }
636
637         else {
638             if ($userid) {
639                 $info{'invalid_username_or_password'} = 1;
640                 C4::Context->_unset_userenv($sessionID);
641             }
642         }
643     }
644     my $insecure = C4::Context->boolean_preference('insecure');
645
646     # finished authentification, now respond
647     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
648     {
649         # successful login
650         unless ($cookie) {
651             $cookie = $query->cookie( CGISESSID => ''
652             );
653         }
654     return ( $userid, $cookie, $sessionID, $flags );
655
656     }
657
658 #
659 #
660 # AUTH rejected, show the login/password template, after checking the DB.
661 #
662 #
663     
664     # get the inputs from the incoming query
665     my @inputs = ();
666     foreach my $name ( param $query) {
667         (next) if ( $name eq 'userid' || $name eq 'password' );
668         my $value = $query->param($name);
669         push @inputs, { name => $name, value => $value };
670     }
671     # get the branchloop, which we need for authentication
672     my $branches = GetBranches();
673     my @branch_loop;
674     for my $branch_hash (keys %$branches) {
675                 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
676     }
677
678     # check that database and koha version are the same
679     # there is no DB version, it's a fresh install,
680     # go to web installer
681     # there is a DB version, compare it to the code version
682     my $kohaversion=C4::Context::KOHAVERSION;
683     # remove the 3 last . to have a Perl number
684     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
685 #     warn "kohaversion : $kohaversion";
686     if (C4::Context->preference('Version') < $kohaversion){
687       if ($type ne 'opac'){
688       warn "Database update needed, redirecting to Installer. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
689         print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
690       } else {
691       warn "OPAC :Database update needed, redirecting to maintenance. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
692         print $query->redirect("/cgi-bin/koha/maintenance.pl");
693       }       
694       exit;
695     }
696     my $template_name;
697     if ( $type eq 'opac' ) {
698         $template_name = "opac-auth.tmpl";
699     }
700     else {
701         $template_name = "auth.tmpl";
702     }
703     my $template = gettemplate( $template_name, $type, $query );
704     $template->param(branchloop => \@branch_loop,);
705     $template->param(
706     login        => 1,
707         INPUTS               => \@inputs,
708         suggestion           => C4::Context->preference("suggestion"),
709         virtualshelves       => C4::Context->preference("virtualshelves"),
710         opaclargeimage       => C4::Context->preference("opaclargeimage"),
711         LibraryName          => C4::Context->preference("LibraryName"),
712         OpacNav              => C4::Context->preference("OpacNav"),
713         opaccredits          => C4::Context->preference("opaccredits"),
714         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
715         opacsmallimage       => C4::Context->preference("opacsmallimage"),
716         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
717         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
718         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
719         opacuserjs           => C4::Context->preference("opacuserjs"),
720
721         intranetcolorstylesheet =>
722           C4::Context->preference("intranetcolorstylesheet"),
723         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
724         IntranetNav        => C4::Context->preference("IntranetNav"),
725         intranetuserjs     => C4::Context->preference("intranetuserjs"),
726         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
727         IndependantBranches     => C4::Context->preference("IndependantBranches"),
728     );
729     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
730
731     my $self_url = $query->url( -absolute => 1 );
732     $template->param(
733         url         => $self_url,
734         LibraryName => => C4::Context->preference("LibraryName"),
735     );
736     $template->param( \%info );
737 #    $cookie = $query->cookie(CGISESSID => $session->id
738 #   );
739     print $query->header(
740                 -type   => 'text/html',
741         -charset => 'utf-8',
742         -cookie => $cookie
743       ),
744       $template->output;
745     exit;
746 }
747
748 sub checkpw {
749
750     my ( $dbh, $userid, $password ) = @_;
751
752     # INTERNAL AUTH
753     my $sth =
754       $dbh->prepare(
755 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
756       );
757     $sth->execute($userid);
758     if ( $sth->rows ) {
759         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
760             $surname, $branchcode, $flags )
761           = $sth->fetchrow;
762         if ( md5_base64($password) eq $md5password ) {
763
764             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
765                 $firstname, $surname, $branchcode, $flags );
766             return 1, $cardnumber;
767         }
768     }
769     $sth =
770       $dbh->prepare(
771 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
772       );
773     $sth->execute($userid);
774     if ( $sth->rows ) {
775         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
776             $surname, $branchcode, $flags )
777           = $sth->fetchrow;
778         if ( md5_base64($password) eq $md5password ) {
779
780             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
781                 $firstname, $surname, $branchcode, $flags );
782             return 1, $userid;
783         }
784     }
785     if (   $userid && $userid eq C4::Context->config('user')
786         && "$password" eq C4::Context->config('pass') )
787     {
788
789 # Koha superuser account
790 #     C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
791         return 2;
792     }
793     if (   $userid && $userid eq 'demo'
794         && "$password" eq 'demo'
795         && C4::Context->config('demo') )
796     {
797
798 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
799 # some features won't be effective : modify systempref, modify MARC structure,
800         return 2;
801     }
802     return 0;
803 }
804
805 sub getuserflags {
806     my $cardnumber = shift;
807     my $dbh        = shift;
808     my $userflags;
809     my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE cardnumber=?");
810     $sth->execute($cardnumber);
811     my ($flags) = $sth->fetchrow;
812     $flags = 0 unless $flags;
813     $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
814     $sth->execute;
815
816     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
817         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
818             $userflags->{$flag} = 1;
819         }
820         else {
821             $userflags->{$flag} = 0;
822         }
823     }
824     return $userflags;
825 }
826
827 sub haspermission {
828     my ( $dbh, $userid, $flagsrequired ) = @_;
829     my $sth = $dbh->prepare("SELECT cardnumber FROM borrowers WHERE userid=?");
830     $sth->execute($userid);
831     my ($cardnumber) = $sth->fetchrow;
832     ($cardnumber) || ( $cardnumber = $userid );
833     my $flags = getuserflags( $cardnumber, $dbh );
834     my $configfile;
835     if ( $userid eq C4::Context->config('user') ) {
836
837         # Super User Account from /etc/koha.conf
838         $flags->{'superlibrarian'} = 1;
839     }
840     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
841
842         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
843         $flags->{'superlibrarian'} = 1;
844     }
845     return $flags if $flags->{superlibrarian};
846     foreach ( keys %$flagsrequired ) {
847         return $flags if $flags->{$_};
848     }
849     return 0;
850 }
851
852 sub getborrowernumber {
853     my ($userid) = @_;
854     my $dbh = C4::Context->dbh;
855     for my $field ( 'userid', 'cardnumber' ) {
856         my $sth =
857           $dbh->prepare("select borrowernumber from borrowers where $field=?");
858         $sth->execute($userid);
859         if ( $sth->rows ) {
860             my ($bnumber) = $sth->fetchrow;
861             return $bnumber;
862         }
863     }
864     return 0;
865 }
866
867 END { }    # module clean-up code here (global destructor)
868 1;
869 __END__
870
871 =back
872
873 =head1 SEE ALSO
874
875 CGI(3)
876
877 C4::Output(3)
878
879 Digest::MD5(3)
880
881 =cut