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