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