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