Merge branch 'master' of /usr/local/git/koha_base.git/
[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             LoginBranchnameShort    => substr((C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"),0,10),
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
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, $envcookie );
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                 my $branches = GetBranches();
555                 my @branchesloop;
556                 foreach my $br ( keys %$branches ) {
557
558                     #           now we work with the treatment of ip
559                     my $domain = $branches->{$br}->{'branchip'};
560                     if ( $domain && $ip =~ /^$domain/ ) {
561                         $branchcode = $branches->{$br}->{'branchcode'};
562
563                         # new op dev : add the branchprinter and branchname in the cookie
564                         $branchprinter = $branches->{$br}->{'branchprinter'};
565                         $branchname    = $branches->{$br}->{'branchname'};
566                     }
567                 }
568                                 
569                                 $session->param('number',$borrowernumber);
570                                 $session->param('id',$userid);
571                                 $session->param('cardnumber',$cardnumber);
572                                 $session->param('firstname',$firstname);
573                                 $session->param('surname',$surname);
574                                 $session->param('branch',$branchcode);
575                                 $session->param('branchname',$branchname);
576                                 $session->param('flags',$userflags);
577                                 $session->param('emailaddress',$emailaddress);
578                 $session->param('ip',$session->remote_addr());
579                                 $session->param('lasttime',time());
580             }
581             elsif ( $return == 2 ) {
582
583                 #We suppose the user is the superlibrarian
584                                 $session->param('number',0);
585                                 $session->param('id',C4::Context->config('user'));
586                                 $session->param('cardnumber',C4::Context->config('user'));
587                                 $session->param('firstname',C4::Context->config('user'));
588                                 $session->param('surname',C4::Context->config('user'),);
589                                 $session->param('branch','NO_LIBRARY_SET');
590                                 $session->param('branchname','NO_LIBRARY_SET');
591                                 $session->param('flags',1);
592                                 $session->param('emailaddress', C4::Context->preference('KohaAdminEmailAddress'));
593                 $session->param('ip',$session->remote_addr());
594                                 $session->param('lasttime',time());
595                         }
596         }
597         else {
598             if ($userid) {
599                 $info{'invalid_username_or_password'} = 1;
600                 C4::Context->_unset_userenv($sessionID);
601             }
602         }
603     }
604     my $insecure = C4::Context->boolean_preference('insecure');
605
606     # finished authentification, now respond
607     if ( $loggedin || $authnotrequired || ( defined($insecure) && $insecure ) )
608     {
609         # successful login
610         unless ($cookie) {
611             $cookie = $query->cookie( CGISESSID => ''
612             );
613         }
614                 return ( $userid, $cookie, $sessionID, $flags );
615
616     }
617
618     # else we have a problem...
619     # get the inputs from the incoming query
620     my @inputs = ();
621     foreach my $name ( param $query) {
622         (next) if ( $name eq 'userid' || $name eq 'password' );
623         my $value = $query->param($name);
624         push @inputs, { name => $name, value => $value };
625     }
626
627     my $template = gettemplate( $template_name, $type, $query );
628     $template->param(
629         INPUTS               => \@inputs,
630         suggestion           => C4::Context->preference("suggestion"),
631         virtualshelves       => C4::Context->preference("virtualshelves"),
632         opaclargeimage       => C4::Context->preference("opaclargeimage"),
633         LibraryName          => C4::Context->preference("LibraryName"),
634         OpacNav              => C4::Context->preference("OpacNav"),
635         opaccredits          => C4::Context->preference("opaccredits"),
636         opacreadinghistory   => C4::Context->preference("opacreadinghistory"),
637         opacsmallimage       => C4::Context->preference("opacsmallimage"),
638         opaclayoutstylesheet => C4::Context->preference("opaclayoutstylesheet"),
639         opaccolorstylesheet  => C4::Context->preference("opaccolorstylesheet"),
640         opaclanguagesdisplay => C4::Context->preference("opaclanguagesdisplay"),
641         opacuserjs           => C4::Context->preference("opacuserjs"),
642
643         intranetcolorstylesheet =>
644           C4::Context->preference("intranetcolorstylesheet"),
645         intranetstylesheet => C4::Context->preference("intranetstylesheet"),
646         IntranetNav        => C4::Context->preference("IntranetNav"),
647         intranetuserjs     => C4::Context->preference("intranetuserjs"),
648         TemplateEncoding   => C4::Context->preference("TemplateEncoding"),
649
650     );
651     $template->param( loginprompt => 1 ) unless $info{'nopermission'};
652
653     my $self_url = $query->url( -absolute => 1 );
654     $template->param(
655         url         => $self_url,
656         LibraryName => => C4::Context->preference("LibraryName"),
657     );
658     $template->param( \%info );
659 #    $cookie = $query->cookie(CGISESSID => $session->id
660 #   );
661     print $query->header(
662         -type   => 'utf-8',
663         -cookie => $cookie
664       ),
665       $template->output;
666     exit;
667 }
668
669 sub checkpw {
670
671     my ( $dbh, $userid, $password ) = @_;
672
673     # INTERNAL AUTH
674     my $sth =
675       $dbh->prepare(
676 "select password,cardnumber,borrowernumber,userid,firstname,surname,branchcode,flags from borrowers where userid=?"
677       );
678     $sth->execute($userid);
679     if ( $sth->rows ) {
680         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
681             $surname, $branchcode, $flags )
682           = $sth->fetchrow;
683         if ( md5_base64($password) eq $md5password ) {
684
685             C4::Context->set_userenv( "$borrowernumber", $userid, $cardnumber,
686                 $firstname, $surname, $branchcode, $flags );
687             return 1, $cardnumber;
688         }
689     }
690     $sth =
691       $dbh->prepare(
692 "select password,cardnumber,borrowernumber,userid, firstname,surname,branchcode,flags from borrowers where cardnumber=?"
693       );
694     $sth->execute($userid);
695     if ( $sth->rows ) {
696         my ( $md5password, $cardnumber, $borrowernumber, $userid, $firstname,
697             $surname, $branchcode, $flags )
698           = $sth->fetchrow;
699         if ( md5_base64($password) eq $md5password ) {
700
701             C4::Context->set_userenv( $borrowernumber, $userid, $cardnumber,
702                 $firstname, $surname, $branchcode, $flags );
703             return 1, $userid;
704         }
705     }
706     if (   $userid && $userid eq C4::Context->config('user')
707         && "$password" eq C4::Context->config('pass') )
708     {
709
710 # Koha superuser account
711 #               C4::Context->set_userenv(0,0,C4::Context->config('user'),C4::Context->config('user'),C4::Context->config('user'),"",1);
712         return 2;
713     }
714     if (   $userid && $userid eq 'demo'
715         && "$password" eq 'demo'
716         && C4::Context->config('demo') )
717     {
718
719 # DEMO => the demo user is allowed to do everything (if demo set to 1 in koha.conf
720 # some features won't be effective : modify systempref, modify MARC structure,
721         return 2;
722     }
723     return 0;
724 }
725
726 sub getuserflags {
727     my $cardnumber = shift;
728     my $dbh        = shift;
729     my $userflags;
730     my $sth = $dbh->prepare("SELECT flags FROM borrowers WHERE cardnumber=?");
731     $sth->execute($cardnumber);
732     my ($flags) = $sth->fetchrow;
733     $flags = 0 unless $flags;
734     $sth = $dbh->prepare("SELECT bit, flag, defaulton FROM userflags");
735     $sth->execute;
736
737     while ( my ( $bit, $flag, $defaulton ) = $sth->fetchrow ) {
738         if ( ( $flags & ( 2**$bit ) ) || $defaulton ) {
739             $userflags->{$flag} = 1;
740         }
741         else {
742             $userflags->{$flag} = 0;
743         }
744     }
745     return $userflags;
746 }
747
748 sub haspermission {
749     my ( $dbh, $userid, $flagsrequired ) = @_;
750     my $sth = $dbh->prepare("SELECT cardnumber FROM borrowers WHERE userid=?");
751     $sth->execute($userid);
752     my ($cardnumber) = $sth->fetchrow;
753     ($cardnumber) || ( $cardnumber = $userid );
754     my $flags = getuserflags( $cardnumber, $dbh );
755     my $configfile;
756     if ( $userid eq C4::Context->config('user') ) {
757
758         # Super User Account from /etc/koha.conf
759         $flags->{'superlibrarian'} = 1;
760     }
761     if ( $userid eq 'demo' && C4::Context->config('demo') ) {
762
763         # Demo user that can do "anything" (demo=1 in /etc/koha.conf)
764         $flags->{'superlibrarian'} = 1;
765     }
766     return $flags if $flags->{superlibrarian};
767     foreach ( keys %$flagsrequired ) {
768         return $flags if $flags->{$_};
769     }
770     return 0;
771 }
772
773 sub getborrowernumber {
774     my ($userid) = @_;
775     my $dbh = C4::Context->dbh;
776     for my $field ( 'userid', 'cardnumber' ) {
777         my $sth =
778           $dbh->prepare("select borrowernumber from borrowers where $field=?");
779         $sth->execute($userid);
780         if ( $sth->rows ) {
781             my ($bnumber) = $sth->fetchrow;
782             return $bnumber;
783         }
784     }
785     return 0;
786 }
787
788 END { }    # module clean-up code here (global destructor)
789 1;
790 __END__
791
792 =back
793
794 =head1 SEE ALSO
795
796 CGI(3)
797
798 C4::Output(3)
799
800 Digest::MD5(3)
801
802 =cut