Ported session storage code to support PostgreSQL
[koha.git] / C4 / Auth.pm
1
2 # -*- tab-width: 8 -*-
3 # NOTE: This file uses 8-character tabs; do not change the tab size!
4
5 package C4::Auth;
6
7 # Copyright 2000-2002 Katipo Communications
8 #
9 # This file is part of Koha.
10 #
11 # Koha is free software; you can redistribute it and/or modify it under the
12 # terms of the GNU General Public License as published by the Free Software
13 # Foundation; either version 2 of the License, or (at your option) any later
14 # version.
15 #
16 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
17 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License along with
21 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
22 # Suite 330, Boston, MA  02111-1307 USA
23
24 use strict;
25 use Digest::MD5 qw(md5_base64);
26 use CGI::Session;
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             IndependantBranches     => C4::Context->preference("IndependantBranches"),
259                         CircAutocompl => C4::Context->preference("CircAutocompl"),
260                         yuipath => C4::Context->preference("yuipath"),
261         );
262     }
263     else {
264         warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' );
265         my $LibraryNameTitle = C4::Context->preference("LibraryName");
266         $LibraryNameTitle =~ s/<(?:\/?)(?:br|p)\s*(?:\/?)>/ /sgi;
267         $LibraryNameTitle =~ s/<(?:[^<>'"]|'(?:[^']*)'|"(?:[^"]*)")*>//sg;
268   $template->param(
269             KohaAdminEmailAddress  => "" . C4::Context->preference("KohaAdminEmailAddress"),
270             suggestion             => "" . C4::Context->preference("suggestion"),
271             virtualshelves         => "" . C4::Context->preference("virtualshelves"),
272             OpacNav                => "" . C4::Context->preference("OpacNav"),
273             opacheader             => "" . C4::Context->preference("opacheader"),
274             opaccredits            => "" . C4::Context->preference("opaccredits"),
275             opacsmallimage         => "" . C4::Context->preference("opacsmallimage"),
276             opaclargeimage         => "" . C4::Context->preference("opaclargeimage"),
277             opaclayoutstylesheet   => "". C4::Context->preference("opaclayoutstylesheet"),
278             opaccolorstylesheet    => "". C4::Context->preference("opaccolorstylesheet"),
279             opaclanguagesdisplay   => "". C4::Context->preference("opaclanguagesdisplay"),
280             opacuserlogin          => "" . C4::Context->preference("opacuserlogin"),
281             opacbookbag            => "" . C4::Context->preference("opacbookbag"),
282             TemplateEncoding       => "". C4::Context->preference("TemplateEncoding"),
283             AmazonContent          => "" . C4::Context->preference("AmazonContent"),
284             LibraryName            => "" . C4::Context->preference("LibraryName"),
285             LibraryNameTitle       => "" . $LibraryNameTitle,
286             LoginBranchcode        => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"),
287             LoginBranchname        => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", 
288             LoginFirstname        => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"),
289             LoginSurname        => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", 
290             OpacPasswordChange     => C4::Context->preference("OpacPasswordChange"),
291             opacreadinghistory     => C4::Context->preference("opacreadinghistory"),
292             opacuserjs             => C4::Context->preference("opacuserjs"),
293             OpacCloud              => C4::Context->preference("OpacCloud"),
294             OpacTopissue           => C4::Context->preference("OpacTopissue"),
295             OpacAuthorities        => C4::Context->preference("OpacAuthorities"),
296             OpacBrowser            => C4::Context->preference("OpacBrowser"),
297             RequestOnOpac          => C4::Context->preference("RequestOnOpac"),
298             reviewson              => C4::Context->preference("reviewson"),
299             hide_marc              => C4::Context->preference("hide_marc"),
300             patronimages           => C4::Context->preference("patronimages"),
301             mylibraryfirst   => C4::Context->preference("SearchMyLibraryFirst"),
302             "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1,
303         );
304     }
305     return ( $template, $borrowernumber, $cookie );
306 }
307
308 =item checkauth
309
310   ($userid, $cookie, $sessionID) = &checkauth($query, $noauth, $flagsrequired, $type);
311
312 Verifies that the user is authorized to run this script.  If
313 the user is authorized, a (userid, cookie, session-id, flags)
314 quadruple is returned.  If the user is not authorized but does
315 not have the required privilege (see $flagsrequired below), it
316 displays an error page and exits.  Otherwise, it displays the
317 login page and exits.
318
319 Note that C<&checkauth> will return if and only if the user
320 is authorized, so it should be called early on, before any
321 unfinished operations (e.g., if you've opened a file, then
322 C<&checkauth> won't close it for you).
323
324 C<$query> is the CGI object for the script calling C<&checkauth>.
325
326 The C<$noauth> argument is optional. If it is set, then no
327 authorization is required for the script.
328
329 C<&checkauth> fetches user and session information from C<$query> and
330 ensures that the user is authorized to run scripts that require
331 authorization.
332
333 The C<$flagsrequired> argument specifies the required privileges
334 the user must have if the username and password are correct.
335 It should be specified as a reference-to-hash; keys in the hash
336 should be the "flags" for the user, as specified in the Members
337 intranet module. Any key specified must correspond to a "flag"
338 in the userflags table. E.g., { circulate => 1 } would specify
339 that the user must have the "circulate" privilege in order to
340 proceed. To make sure that access control is correct, the
341 C<$flagsrequired> parameter must be specified correctly.
342
343 The C<$type> argument specifies whether the template should be
344 retrieved from the opac or intranet directory tree.  "opac" is
345 assumed if it is not specified; however, if C<$type> is specified,
346 "intranet" is assumed if it is not "opac".
347
348 If C<$query> does not have a valid session ID associated with it
349 (i.e., the user has not logged in) or if the session has expired,
350 C<&checkauth> presents the user with a login page (from the point of
351 view of the original script, C<&checkauth> does not return). Once the
352 user has authenticated, C<&checkauth> restarts the original script
353 (this time, C<&checkauth> returns).
354
355 The login page is provided using a HTML::Template, which is set in the
356 systempreferences table or at the top of this file. The variable C<$type>
357 selects which template to use, either the opac or the intranet 
358 authentification template.
359
360 C<&checkauth> returns a user ID, a cookie, and a session ID. The
361 cookie should be sent back to the browser; it verifies that the user
362 has authenticated.
363
364 =cut
365
366 sub checkauth {
367     my $query = shift;
368   # warn "Checking Auth";
369     # $authnotrequired will be set for scripts which will run without authentication
370     my $authnotrequired = shift;
371     my $flagsrequired   = shift;
372     my $type            = shift;
373     $type = 'opac' unless $type;
374
375     my $dbh     = C4::Context->dbh;
376     my $timeout = C4::Context->preference('timeout');
377     $timeout = 600 unless $timeout;
378
379
380     # If Version syspref is unavailable, it means Koha is beeing installed,
381     # and so we must redirect to OPAC maintenance page or to the WebInstaller
382     #warn "about to check version";
383     unless (C4::Context->preference('Version')) {
384       if ($type ne 'opac') {
385         warn "Install required, redirecting to Installer";
386         print $query->redirect("/cgi-bin/koha/installer/install.pl");
387       } 
388       else {
389         warn "OPAC Install required, redirecting to maintenance";
390         print $query->redirect("/cgi-bin/koha/maintenance.pl");
391       }
392       exit;
393     }
394
395
396     # state variables
397     my $loggedin = 0;
398     my %info;
399     my ( $userid, $cookie, $sessionID, $flags );
400     my $logout = $query->param('logout.x');
401     if ( $userid = $ENV{'REMOTE_USER'} ) {
402         # Using Basic Authentication, no cookies required
403         $cookie = $query->cookie(
404             -name    => 'CGISESSID',
405             -value   => '',
406             -expires => ''
407         );
408         $loggedin = 1;
409     }
410     elsif ( $sessionID = $query->cookie("CGISESSID")) {
411                 my $storage_method = C4::Context->preference('SessionStorage');
412                 my $session;
413                 if ($storage_method eq 'mysql'){
414                     $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
415                 }
416                 elsif ($storage_method eq 'Pg') {
417                         $session = new CGI::Session("driver:PostgreSQL", $sessionID, {Handle=>$dbh});
418                 }
419                 else {
420                         # catch all defaults to tmp should work on all systems
421                         $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});
422                 }
423         C4::Context->_new_userenv($sessionID);
424         if ($session){
425             C4::Context::set_userenv(
426                 $session->param('number'),       $session->param('id'),
427                 $session->param('cardnumber'),   $session->param('firstname'),
428                 $session->param('surname'),      $session->param('branch'),
429                 $session->param('branchname'),   $session->param('flags'),
430                 $session->param('emailaddress'), $session->param('branchprinter')
431             );
432 #             warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
433 #                 ".$session->param('surname').",      ".$session->param('branch');
434         }
435         my $ip;
436         my $lasttime;
437         if ($session) {
438           $ip = $session->param('ip');
439           $lasttime = $session->param('lasttime');
440                 $userid = $session->param('id');
441         }
442         
443     
444         if ($logout) {
445
446             # voluntary logout the user
447
448             $session->flush;      
449                         $session->delete();
450             C4::Context->_unset_userenv($sessionID);
451             $sessionID = undef;
452             $userid    = undef;
453             open L, ">>/tmp/sessionlog";
454             my $time = localtime( time() );
455             printf L "%20s from %16s logged out at %30s (manually).\n", $userid,
456               $ip, $time;
457             close L;
458         }
459         if ($userid) {
460             if ( $lasttime < time() - $timeout ) {
461                 # timed logout
462                 $info{'timed_out'} = 1;
463                 $session->delete();
464                 C4::Context->_unset_userenv($sessionID);
465                 $userid    = undef;
466                 $sessionID = undef;
467                 open L, ">>/tmp/sessionlog";
468                 my $time = localtime( time() );
469                 printf L "%20s from %16s logged out at %30s (inactivity).\n",
470                   $userid, $ip, $time;
471                 close L;
472             }
473             elsif ( $ip ne $ENV{'REMOTE_ADDR'} ) {
474                 # Different ip than originally logged in from
475                 $info{'oldip'}        = $ip;
476                 $info{'newip'}        = $ENV{'REMOTE_ADDR'};
477                 $info{'different_ip'} = 1;
478         $session->delete();
479                 C4::Context->_unset_userenv($sessionID);
480                 $sessionID = undef;
481                 $userid    = undef;
482                 open L, ">>/tmp/sessionlog";
483                 my $time = localtime( time() );
484                 printf L
485 "%20s from logged out at %30s (ip changed from %16s to %16s).\n",
486                   $userid, $time, $ip, $info{'newip'};
487                 close L;
488             }
489             else {
490                 $cookie = $query->cookie( CGISESSID => $session->id );
491                 $session->param('lasttime',time());
492                 $flags = haspermission( $dbh, $userid, $flagsrequired );
493                 if ($flags) {
494                     $loggedin = 1;
495                 }
496                 else {
497                     $info{'nopermission'} = 1;
498                 }
499             }
500         }
501     }
502     unless ($userid) {
503                 my $storage_method = C4::Context->preference('SessionStorage');
504                 my $session;
505                 if ($storage_method eq 'mysql'){
506                     $session = new CGI::Session("driver:MySQL", $sessionID, {Handle=>$dbh});
507                 }
508                 elsif ($storage_method eq 'Pg') {
509                         $session = new CGI::Session("driver:PostgreSQL", $sessionID, {Handle=>$dbh});
510                 }
511                 else {
512                         # catch all defaults to tmp should work on all systems
513                         $session = new CGI::Session("driver:File", $sessionID, {Directory=>'/tmp'});                    
514                 }
515
516         my $sessionID;
517                 if ($session) {
518                         $sessionID = $session->id;
519                 }
520         $userid    = $query->param('userid');
521         C4::Context->_new_userenv($sessionID);
522         my $password = $query->param('password');
523         C4::Context->_new_userenv($sessionID);
524         my ( $return, $cardnumber ) = checkpw( $dbh, $userid, $password );
525         if ($return) {
526             open L, ">>/tmp/sessionlog";
527             my $time = localtime( time() );
528             printf L "%20s from %16s logged in  at %30s.\n", $userid,
529               $ENV{'REMOTE_ADDR'}, $time;
530             close L;
531             $cookie = $query->cookie(CGISESSID => $sessionID);
532             if ( $flags = haspermission( $dbh, $userid, $flagsrequired ) ) {
533                 $loggedin = 1;
534             }
535             else {
536                 $info{'nopermission'} = 1;
537                 C4::Context->_unset_userenv($sessionID);
538             }
539             if ( $return == 1 ) {
540                 my (
541                     $borrowernumber, $firstname,  $surname,
542                     $userflags,      $branchcode, $branchname,
543                     $branchprinter,  $emailaddress
544                 );
545                 my $sth =
546                   $dbh->prepare(
547 "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=?"
548                   );
549                 $sth->execute($userid);
550                 (
551                     $borrowernumber, $firstname,  $surname,
552                     $userflags,      $branchcode, $branchname,
553                     $branchprinter,  $emailaddress
554                   )
555                   = $sth->fetchrow
556                   if ( $sth->rows );
557
558 #         warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
559                 unless ( $sth->rows ) {
560                     my $sth =
561                       $dbh->prepare(
562 "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=?"
563                       );
564                     $sth->execute($cardnumber);
565                     (
566                         $borrowernumber, $firstname,  $surname,
567                         $userflags,      $branchcode, $branchname,
568                         $branchprinter,  $emailaddress
569                       )
570                       = $sth->fetchrow
571                       if ( $sth->rows );
572
573 #           warn "$cardnumber,$borrowernumber,$userid,$firstname,$surname,$userflags,$branchcode,$emailaddress";
574                     unless ( $sth->rows ) {
575                         $sth->execute($userid);
576                         (
577                             $borrowernumber, $firstname, $surname, $userflags,
578                             $branchcode, $branchname, $branchprinter, $emailaddress
579                           )
580                           = $sth->fetchrow
581                           if ( $sth->rows );
582                     }
583                 }
584
585 # 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.
586                 my $ip       = $ENV{'REMOTE_ADDR'};
587                 # if they specify at login, use that
588                 if ($query->param('branch')) {
589                     $branchcode  = $query->param('branch');
590                     $branchname = GetBranchName($branchcode);
591                 }
592                 my $branches = GetBranches();
593                 my @branchesloop;
594                 foreach my $br ( keys %$branches ) {
595                     #     now we work with the treatment of ip
596                     my $domain = $branches->{$br}->{'branchip'};
597                     if ( $domain && $ip =~ /^$domain/ ) {
598                         $branchcode = $branches->{$br}->{'branchcode'};
599
600                         # new op dev : add the branchprinter and branchname in the cookie
601                         $branchprinter = $branches->{$br}->{'branchprinter'};
602                         $branchname    = $branches->{$br}->{'branchname'};
603                     }
604                 }
605                 $session->param('number',$borrowernumber);
606                 $session->param('id',$userid);
607                 $session->param('cardnumber',$cardnumber);
608                 $session->param('firstname',$firstname);
609                 $session->param('surname',$surname);
610                 $session->param('branch',$branchcode);
611                 $session->param('branchname',$branchname);
612                 $session->param('flags',$userflags);
613                 $session->param('emailaddress',$emailaddress);
614                 $session->param('ip',$session->remote_addr());
615                 $session->param('lasttime',time());
616 #            warn       "".$session->param('cardnumber').",   ".$session->param('firstname').",
617 #                 ".$session->param('surname').",      ".$session->param('branch');
618             }
619             elsif ( $return == 2 ) {
620                 #We suppose the user is the superlibrarian
621                         $session->param('number',0);
622                         $session->param('id',C4::Context->config('user'));
623                         $session->param('cardnumber',C4::Context->config('user'));
624                         $session->param('firstname',C4::Context->config('user'));
625                         $session->param('surname',C4::Context->config('user'));
626                         $session->param('branch','NO_LIBRARY_SET');
627                         $session->param('branchname','NO_LIBRARY_SET');
628                         $session->param('flags',1);
629                         $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
630                         $session->param('ip',$session->remote_addr());
631                         $session->param('lasttime',time());
632                 }
633                 if ($session){
634                     C4::Context::set_userenv(
635                         $session->param('number'),       $session->param('id'),
636                         $session->param('cardnumber'),   $session->param('firstname'),
637                         $session->param('surname'),      $session->param('branch'),
638                         $session->param('branchname'),   $session->param('flags'),
639                         $session->param('emailaddress'), $session->param('branchprinter')
640                     );
641                 }
642         }
643
644         else {
645             if ($userid) {
646                 $info{'invalid_username_or_password'} = 1;
647                 C4::Context->_unset_userenv($sessionID);
648             }
649         }
650     }
651     my $insecure = C4::Context->boolean_preference('insecure');
652
653     # finished authentification, now respond
654     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
655     {
656         # successful login
657         unless ($cookie) {
658             $cookie = $query->cookie( CGISESSID => ''
659             );
660         }
661     return ( $userid, $cookie, $sessionID, $flags );
662
663     }
664
665 #
666 #
667 # AUTH rejected, show the login/password template, after checking the DB.
668 #
669 #
670     
671     # get the inputs from the incoming query
672     my @inputs = ();
673     foreach my $name ( param $query) {
674         (next) if ( $name eq 'userid' || $name eq 'password' );
675         my $value = $query->param($name);
676         push @inputs, { name => $name, value => $value };
677     }
678     # get the branchloop, which we need for authentication
679     my $branches = GetBranches();
680     my @branch_loop;
681     for my $branch_hash (keys %$branches) {
682                 push @branch_loop, {branchcode => "$branch_hash", branchname => $branches->{$branch_hash}->{'branchname'}, };
683     }
684
685     # check that database and koha version are the same
686     # there is no DB version, it's a fresh install,
687     # go to web installer
688     # there is a DB version, compare it to the code version
689     my $kohaversion=C4::Context::KOHAVERSION;
690     # remove the 3 last . to have a Perl number
691     $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
692 #     warn "kohaversion : $kohaversion";
693     if (C4::Context->preference('Version') < $kohaversion){
694       if ($type ne 'opac'){
695       warn "Database update needed, redirecting to Installer. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
696         print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3");
697       } else {
698       warn "OPAC :Database update needed, redirecting to maintenance. Database is ".C4::Context->preference('Version')." and Koha is : ".C4::Context->config("kohaversion");
699         print $query->redirect("/cgi-bin/koha/maintenance.pl");
700       }       
701       exit;
702     }
703     my $template_name;
704     if ( $type eq 'opac' ) {
705         $template_name = "opac-auth.tmpl";
706     }
707     else {
708         $template_name = "auth.tmpl";
709     }
710     my $template = gettemplate( $template_name, $type, $query );
711     $template->param(branchloop => \@branch_loop,);
712     $template->param(
713     login        => 1,
714         INPUTS               => \@inputs,
715         suggestion           => C4::Context->preference("suggestion"),
716         virtualshelves       => C4::Context->preference("virtualshelves"),
717         opaclargeimage       => C4::Context->preference("opaclargeimage"),
718         LibraryName          => C4::Context->preference("LibraryName"),
719         OpacNav              => C4::Context->preference("OpacNav"),
720         opaccredits          => C4::Context->preference("opaccredits"),
721         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
722         opacsmallimage       => C4::Context->preference("opacsmallimage"),
723         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
724         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
725         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
726         opacuserjs           => C4::Context->preference("opacuserjs"),
727
728         intranetcolorstylesheet =>
729           C4::Context->preference("intranetcolorstylesheet"),
730         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
731         IntranetNav        => C4::Context->preference("IntranetNav"),
732         intranetuserjs     => C4::Context->preference("intranetuserjs"),
733         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
734         IndependantBranches     => C4::Context->preference("IndependantBranches"),
735     );
736     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
737
738     my $self_url = $query->url( -absolute => 1 );
739     $template->param(
740         url         => $self_url,
741         LibraryName => => C4::Context->preference("LibraryName"),
742     );
743     $template->param( \%info );
744 #    $cookie = $query->cookie(CGISESSID => $session->id
745 #   );
746     print $query->header(
747                 -type   => 'text/html',
748         -charset => 'utf-8',
749         -cookie => $cookie
750       ),
751       $template->output;
752     exit;
753 }
754
755 sub checkpw {
756
757     my ( $dbh, $userid, $password ) = @_;
758
759     # INTERNAL AUTH
760     my $sth =
761       $dbh->prepare(
762 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
763       );
764     $sth->execute($userid);
765     if ( $sth->rows ) {
766         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
767             $surname, $branchcode, $flags )
768           = $sth->fetchrow;
769         if ( md5_base64($password) eq $md5password ) {
770
771             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
772                 $firstname, $surname, $branchcode, $flags );
773             return 1, $cardnumber;
774         }
775     }
776     $sth =
777       $dbh->prepare(
778 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
779       );
780     $sth->execute($userid);
781     if ( $sth->rows ) {
782         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
783             $surname, $branchcode, $flags )
784           = $sth->fetchrow;
785         if ( md5_base64($password) eq $md5password ) {
786
787             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
788                 $firstname, $surname, $branchcode, $flags );
789             return 1, $userid;
790         }
791     }
792     if (   $userid && $userid eq C4::Context->config('user')
793         && "$password" eq C4::Context->config('pass') )
794     {
795
796 # Koha superuser account
797 #     C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
798         return 2;
799     }
800     if (   $userid && $userid eq 'demo'
801         && "$password" eq 'demo'
802         && C4::Context->config('demo') )
803     {
804
805 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
806 # some features won't be effective : modify systempref, modify MARC structure,
807         return 2;
808     }
809     return 0;
810 }
811
812 sub getuserflags {
813     my $cardnumber = shift;
814     my $dbh        = shift;
815     my $userflags;
816     my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE cardnumber=?");
817     $sth->execute($cardnumber);
818     my ($flags) = $sth->fetchrow;
819     $flags = 0 unless $flags;
820     $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
821     $sth->execute;
822
823     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
824         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
825             $userflags->{$flag} = 1;
826         }
827         else {
828             $userflags->{$flag} = 0;
829         }
830     }
831     return $userflags;
832 }
833
834 sub haspermission {
835     my ( $dbh, $userid, $flagsrequired ) = @_;
836     my $sth = $dbh->prepare("SELECT cardnumber FROM borrowers WHERE userid=?");
837     $sth->execute($userid);
838     my ($cardnumber) = $sth->fetchrow;
839     ($cardnumber) || ( $cardnumber = $userid );
840     my $flags = getuserflags( $cardnumber, $dbh );
841     my $configfile;
842     if ( $userid eq C4::Context->config('user') ) {
843
844         # Super User Account from /etc/koha.conf
845         $flags->{'superlibrarian'} = 1;
846     }
847     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
848
849         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
850         $flags->{'superlibrarian'} = 1;
851     }
852     return $flags if $flags->{superlibrarian};
853     foreach ( keys %$flagsrequired ) {
854         return $flags if $flags->{$_};
855     }
856     return 0;
857 }
858
859 sub getborrowernumber {
860     my ($userid) = @_;
861     my $dbh = C4::Context->dbh;
862     for my $field ( 'userid', 'cardnumber' ) {
863         my $sth =
864           $dbh->prepare("select borrowernumber from borrowers where $field=?");
865         $sth->execute($userid);
866         if ( $sth->rows ) {
867             my ($bnumber) = $sth->fetchrow;
868             return $bnumber;
869         }
870     }
871     return 0;
872 }
873
874 END { }    # module clean-up code here (global destructor)
875 1;
876 __END__
877
878 =back
879
880 =head1 SEE ALSO
881
882 CGI(3)
883
884 C4::Output(3)
885
886 Digest::MD5(3)
887
888 =cut