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