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