File Coverage

blib/lib/Lemonldap/NG/Common/CGI.pm
Criterion Covered Total %
statement 77 187 41.1
branch 16 76 21.0
condition 7 43 16.2
subroutine 16 31 51.6
pod 2 19 10.5
total 118 356 33.1


line stmt bran cond sub pod time code
1             ## @file
2             # Base package for all Lemonldap::NG CGI
3              
4             ## @class
5             # Base class for all Lemonldap::NG CGI
6             package Lemonldap::NG::Common::CGI;
7              
8 1     1   17938 use strict;
  1         2  
  1         46  
9              
10 1     1   5 use File::Basename;
  1         3  
  1         113  
11 1     1   669 use MIME::Base64;
  1         813  
  1         70  
12 1     1   499 use Time::Local;
  1         1851  
  1         92  
13 1     1   314376 use CGI;
  1         16836  
  1         6  
14 1     1   612 use utf8;
  1         9  
  1         4  
15 1     1   539 use Encode;
  1         8075  
  1         83  
16 1     1   487 use Net::CIDR::Lite;
  1         3015  
  1         111  
17              
18             #parameter syslog Indicates syslog facility for logging user actions
19              
20             our $VERSION = '1.4.0';
21             our $_SUPER;
22             our @ISA;
23              
24             BEGIN {
25 1 50   1   5 if ( exists $ENV{MOD_PERL} ) {
26 0 0 0     0 if ( $ENV{MOD_PERL_API_VERSION} and $ENV{MOD_PERL_API_VERSION} >= 2 ) {
27 0         0 eval 'use constant MP => 2;';
28             }
29             else {
30 0         0 eval 'use constant MP => 1;';
31             }
32             }
33             else {
34 1     1   58 eval 'use constant MP => 0;';
  1         5  
  1         9  
  1         45  
35             }
36 1         2 $_SUPER = 'CGI';
37 1         2177 @ISA = ('CGI');
38             }
39              
40             sub import {
41 1     1   135 my $pkg = shift;
42 1 50 33     18 if ( $pkg eq __PACKAGE__ and @_ and $_[0] eq "fastcgi" ) {
      33        
43 0         0 eval 'use CGI::Fast';
44 0 0       0 die($@) if ($@);
45 0         0 unshift @ISA, 'CGI::Fast';
46 0         0 $_SUPER = 'CGI::Fast';
47             }
48             }
49              
50             ## @cmethod Lemonldap::NG::Common::CGI new(@p)
51             # Constructor: launch CGI::new() then secure parameters since CGI store them at
52             # the root of the object.
53             # @param p arguments for CGI::new()
54             # @return new Lemonldap::NG::Common::CGI object
55             sub new {
56 3     3 1 6595 my $class = shift;
57 3 50       20 my $self = $_SUPER->new(@_) or return undef;
58 3         4121 $self->{_prm} = {};
59 3         12 my @tmp = $self->param();
60 3         39 foreach (@tmp) {
61 0         0 $self->{_prm}->{$_} = $self->param($_);
62 0         0 $self->delete($_);
63             }
64 3         8 $self->{lang} = extract_lang();
65 3         12 bless $self, $class;
66 3         17 return $self;
67             }
68              
69             ## @method scalar param(string s, scalar newValue)
70             # Return the wanted parameter issued of GET or POST request. If $s is not set,
71             # return the list of parameters names
72             # @param $s name of the parameter
73             # @param $newValue if set, the parameter will be set to his value
74             # @return datas passed by GET or POST method
75             sub param {
76 0     0 0 0 my ( $self, $p, $v ) = @_;
77 0 0       0 $self->{_prm}->{$p} = $v if ($v);
78 0 0       0 unless ( defined $p ) {
79 0         0 return keys %{ $self->{_prm} };
  0         0  
80             }
81 0         0 return $self->{_prm}->{$p};
82             }
83              
84             ## @method scalar rparam(string s)
85             # Return a reference to a parameter
86             # @param $s name of the parameter
87             # @return ref to parameter data
88             sub rparam {
89 0     0 0 0 my ( $self, $p ) = @_;
90 0 0       0 return $self->{_prm}->{$p} ? \$self->{_prm}->{$p} : undef;
91             }
92              
93             ## @method void lmLog(string mess, string level)
94             # Log subroutine. Use Apache::Log in ModPerl::Registry context else simply
95             # print on STDERR non debug messages.
96             # @param $mess Text to log
97             # @param $level Level (debug|info|notice|error)
98             sub lmLog {
99 2     2 0 3 my ( $self, $mess, $level ) = @_;
100 2         3 my $call;
101 2 50       4 if ( $level eq 'debug' ) {
102 2 50       7 $mess = ( ref($self) ? ref($self) : $self ) . ": $mess";
103             }
104             else {
105 0         0 my @tmp = caller();
106 0         0 $call = "$tmp[1] $tmp[2]:";
107             }
108 2 50 50     9 if ( $self->r and MP() ) {
109 0 0       0 $self->abort( "Level is required",
110             'the parameter "level" is required when lmLog() is used' )
111             unless ($level);
112 0         0 if ( MP() == 2 ) {
113             require Apache2::Log;
114             Apache2::ServerRec->log->debug($call) if ($call);
115             Apache2::ServerRec->log->$level($mess);
116             }
117             else {
118 0 0       0 Apache->server->log->debug($call) if ($call);
119 0         0 Apache->server->log->$level($mess);
120             }
121             }
122             else {
123 2 100       19 $self->{hideLogLevels} = 'debug|info'
124             unless defined( $self->{hideLogLevels} );
125 2         30 my $re = qr/^(?:$self->{hideLogLevels})$/;
126 2 50 33     6 print STDERR "$call\n" if ( $call and 'debug' !~ $re );
127 2 50       15 print STDERR "[$level] $mess\n" unless ( $level =~ $re );
128             }
129             }
130              
131             ## @method void setApacheUser(string user)
132             # Set user for Apache logs in ModPerl::Registry context. Does nothing else.
133             # @param $user data to set as user in Apache logs
134             sub setApacheUser {
135 0     0 0 0 my ( $self, $user ) = @_;
136 0 0 0     0 if ( $self->r and MP() ) {
137 0         0 $self->lmLog( "Inform Apache about the user connected", 'debug' );
138 0         0 if ( MP() == 2 ) {
139             require Apache2::Connection;
140             $self->r->user($user);
141             }
142             else {
143 0         0 $self->r->connection->user($user);
144             }
145             }
146 0         0 $ENV{REMOTE_USER} = $user;
147             }
148              
149             ##@method string getApacheHtdocsPath()
150             # Return absolute path to the htdocs directory where the current script is
151             # @return path string
152             sub getApacheHtdocsPath {
153 0   0 0 0 0 return dirname( $ENV{SCRIPT_FILENAME} || $0 );
154             }
155              
156             ## @method void soapTest(string soapFunctions, object obj)
157             # Check if request is a SOAP request. If it is, launch
158             # Lemonldap::NG::Common::CGI::SOAPServer and exit. Else simply return.
159             # @param $soapFunctions list of authorized functions.
160             # @param $obj optional object that will receive SOAP requests
161             sub soapTest {
162 0     0 0 0 my ( $self, $soapFunctions, $obj ) = @_;
163              
164             # If non form encoded datas are posted, we call SOAP Services
165 0 0       0 if ( $ENV{HTTP_SOAPACTION} ) {
166             require
167 0         0 Lemonldap::NG::Common::CGI::SOAPServer; #link protected dispatcher
168             require
169 0         0 Lemonldap::NG::Common::CGI::SOAPService; #link protected soapService
170 0 0       0 my @func = (
171             ref($soapFunctions) ? @$soapFunctions : split /\s+/,
172             $soapFunctions
173             );
174 0   0     0 my $dispatcher =
175             Lemonldap::NG::Common::CGI::SOAPService->new( $obj || $self, @func );
176 0         0 Lemonldap::NG::Common::CGI::SOAPServer->dispatch_to($dispatcher)
177             ->handle($self);
178 0         0 $self->quit();
179             }
180             }
181              
182             ## @method string header_public(string filename)
183             # Implements the "304 Not Modified" HTTP mechanism.
184             # If HTTP request contains an "If-Modified-Since" header and if
185             # $filename was not modified since, prints the "304 Not Modified" response and
186             # exit. Else, launch CGI::header() with "Cache-Control" and "Last-Modified"
187             # headers.
188             # @param $filename Optional name of the reference file. Default
189             # $ENV{SCRIPT_FILENAME}.
190             # @return Common Gateway Interface standard response header
191             sub header_public {
192 1     1 1 1 my $self = shift;
193 1         2 my $filename = shift;
194 1   33     4 $filename ||= $ENV{SCRIPT_FILENAME};
195 1         28 my @tmp = stat($filename);
196 1         2 my $date = $tmp[9];
197 1         9 my $hd = gmtime($date);
198 1         35 $hd =~ s/^(\w+)\s+(\w+)\s+(\d+)\s+([\d:]+)\s+(\d+)$/$1, $3 $2 $5 $4 GMT/;
199 1         2 my $year = $5;
200 1         2 my $cm = $2;
201              
202             # TODO: Remove TODO_ for stable releases
203 1 50       5 if ( my $ref = $ENV{HTTP_IF_MODIFIED_SINCE} ) {
204 0         0 my %month = (
205             jan => 0,
206             feb => 1,
207             mar => 2,
208             apr => 3,
209             may => 4,
210             jun => 5,
211             jul => 6,
212             aug => 7,
213             sep => 8,
214             oct => 9,
215             nov => 10,
216             dec => 11
217             );
218 0 0       0 if ( $ref =~ /^\w+,\s+(\d+)\s+(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)/ ) {
219 0         0 my $m = $month{ lc($2) };
220 0 0       0 $year-- if ( $m > $month{ lc($cm) } );
221 0         0 $ref = timegm( $6, $5, $4, $1, $m, $3 );
222 0 0       0 if ( $ref == $date ) {
223 0         0 print $self->SUPER::header( -status => '304 Not Modified', @_ );
224 0         0 $self->quit();
225             }
226             }
227             }
228 1         12 return $self->SUPER::header(
229             '-Last-Modified' => $hd,
230             '-Cache-Control' => 'public; must-revalidate; max-age=1800',
231             @_
232             );
233             }
234              
235             ## @method void abort(string title, string text)
236             # Display an error message and exit.
237             # Used instead of die() in Lemonldap::NG CGIs.
238             # @param title Title of the error message
239             # @param text Optional text. Default: "See Apache's logs"
240             sub abort {
241 0     0 0 0 my $self = shift;
242 0         0 my $cgi = CGI->new();
243 0         0 my ( $t1, $t2 ) = @_;
244              
245             # Default message
246 0   0     0 $t2 ||= "See Apache's logs";
247              
248             # Change \n into
for HTML
249 0         0 my $t2html = $t2;
250 0         0 $t2html =~ s#\n#
#g;
251              
252 0         0 print $cgi->header( -type => 'text/html; charset=utf-8', );
253 0         0 print $cgi->start_html(
254             -title => $t1,
255             -encoding => 'utf8',
256             -style => {
257             -code => '
258             body{
259             background:#000;
260             color:#fff;
261             padding:10px 50px;
262             font-family:sans-serif;
263             }
264             a {
265             text-decoration:none;
266             color:#fff;
267             }
268             '
269             },
270             );
271 0         0 print "

$t1

$t2html

";
272 0         0 print
273             '
LemonLDAP::NG
';
274 0   0     0 print STDERR ( ref($self) || $self ) . " error: $t1, $t2\n";
275 0         0 print $cgi->end_html();
276 0         0 $self->quit();
277             }
278              
279             ##@method private void startSyslog()
280             # Open syslog connection.
281             sub startSyslog {
282 0     0 0 0 my $self = shift;
283 0 0       0 return if ( $self->{_syslog} );
284 0         0 eval {
285 0         0 require Sys::Syslog;
286 0         0 Sys::Syslog->import(':standard');
287 0         0 openlog( 'lemonldap-ng', 'ndelay,pid', $self->{syslog} );
288             };
289 0 0       0 $self->abort( "Unable to use syslog", $@ ) if ($@);
290 0         0 $self->{_syslog} = 1;
291             }
292              
293             ##@method void userLog(string mess, string level)
294             # Log user actions on Apache logs or syslog.
295             # @param $mess string to log
296             # @param $level level of log message
297             sub userLog {
298 0     0 0 0 my ( $self, $mess, $level ) = @_;
299 0 0       0 if ( $self->{syslog} ) {
300 0         0 $self->startSyslog();
301 0         0 $level =~ s/^warn$/warning/;
302 0   0     0 syslog( $level || 'notice', $mess );
303             }
304             else {
305 0         0 $self->lmLog( $mess, $level );
306             }
307             }
308              
309             ##@method void userInfo(string mess)
310             # Log non important user actions. Alias for userLog() with facility "info".
311             # @param $mess string to log
312             sub userInfo {
313 0     0 0 0 my ( $self, $mess ) = @_;
314 0         0 $mess = "Lemonldap::NG : $mess (" . $self->ipAddr . ")";
315 0         0 $self->userLog( $mess, 'info' );
316             }
317              
318             ##@method void userNotice(string mess)
319             # Log user actions like access and logout. Alias for userLog() with facility
320             # "notice".
321             # @param $mess string to log
322             sub userNotice {
323 0     0 0 0 my ( $self, $mess ) = @_;
324 0         0 $mess = "Lemonldap::NG : $mess (" . $self->ipAddr . ")";
325 0         0 $self->userLog( $mess, 'notice' );
326             }
327              
328             ##@method void userError(string mess)
329             # Log user errors like "bad password". Alias for userLog() with facility
330             # "warn".
331             # @param $mess string to log
332             sub userError {
333 0     0 0 0 my ( $self, $mess ) = @_;
334 0         0 $mess = "Lemonldap::NG : $mess (" . $self->ipAddr . ")";
335 0         0 $self->userLog( $mess, 'warn' );
336             }
337              
338             ## @method protected scalar _sub(string sub, array p)
339             # Launch $self->{$sub} if defined, else launch $self->$sub.
340             # @param $sub name of the sub to launch
341             # @param @p parameters for the sub
342             sub _sub {
343 2     2   3374 my ( $self, $sub, @p ) = @_;
344 2 100       6 if ( $self->{$sub} ) {
345 1         7 $self->lmLog( "processing to custom sub $sub", 'debug' );
346 1         1 return &{ $self->{$sub} }( $self, @p );
  1         3  
347             }
348             else {
349 1         8 $self->lmLog( "processing to sub $sub", 'debug' );
350 1         4 return $self->$sub(@p);
351             }
352             }
353              
354             ##@method string extract_lang
355             #@return array of user's preferred languages (two letters)
356             sub extract_lang {
357 6     6 0 357 my $self = shift;
358              
359 6   100     63 my @langs = split /,\s*/, ( shift || $ENV{HTTP_ACCEPT_LANGUAGE} || "" );
360 6         9 my @res = ();
361              
362 6         10 foreach (@langs) {
363              
364             # Languages are supposed to be sorted by preference
365 12         25 my $lang = ( split /;/ )[0];
366              
367             # Take first part of lang code (part before -)
368 12         17 $lang = ( split /-/, $lang )[0];
369              
370             # Go to next if lang was already added
371 12 100       72 next if grep( /$lang/, @res );
372              
373             # Store lang only if size is 2 characters
374 8 50       21 push @res, $lang if ( length($lang) == 2 );
375             }
376              
377 6         27 return \@res;
378             }
379              
380             ##@method void translate_template(string text_ref, string lang)
381             # translate_template is used as an HTML::Template filter to tranlate strings in
382             # the wanted language
383             #@param text_ref reference to the string to translate
384             #@param lang optionnal language wanted. Falls to browser language instead.
385             #@return
386             sub translate_template {
387 0     0 0   my $self = shift;
388 0           my $text_ref = shift;
389              
390             # Decode UTF-8
391 0 0         utf8::decode($$text_ref) unless ( $ENV{FCGI_ROLE} );
392              
393             # Test if a translation is available for the selected language
394             # If not available, return the first translated string
395             #
396 0           foreach ( @{ $self->{lang} } ) {
  0            
397 0 0         if ( $$text_ref =~ m/$_=\"(.*?)\"/ ) {
398 0           $$text_ref =~ s//$1/gx;
399 0           return;
400             }
401             }
402 0           $$text_ref =~ s//$1/gx;
403             }
404              
405             ##@method void session_template(string text_ref)
406             # session_template is used as an HTML::Template filter to replace session info
407             # by their value
408             #@param text_ref reference to the string to translate
409             #@return
410             sub session_template {
411 0     0 0   my $self = shift;
412 0           my $text_ref = shift;
413              
414             # Replace session information
415 0           $$text_ref =~ s/\$(\w+)/decode("utf8",$self->{sessionInfo}->{$1})/ge;
  0            
416             }
417              
418             ## @method private void quit()
419             # Simply exit.
420             sub quit {
421 0     0 0   my $self = shift;
422 0 0         if ( $_SUPER eq 'CGI::Fast' ) {
423 0           next LMAUTH;
424             }
425             else {
426 0           exit;
427             }
428             }
429              
430             ##@method string ipAddr()
431             # Retrieve client IP address from remote address or X-FORWARDED-FOR header
432             #@return client IP
433             sub ipAddr {
434 0     0 0   my $self = shift;
435              
436 0 0         unless ( $self->{ipAddr} ) {
437 0           $self->{ipAddr} = $ENV{REMOTE_ADDR};
438 0 0         if ( my $xheader = $ENV{HTTP_X_FORWARDED_FOR} ) {
439 0 0 0       if ( $self->{trustedProxies} =~ /\*/
    0          
440             or $self->{useXForwardedForIP} )
441             {
442 0 0         $self->{ipAddr} = $1 if ( $xheader =~ /^([^,]*)/ );
443             }
444             elsif ( $self->{trustedProxies} ) {
445 0           my $localIP =
446             Net::CIDR::Lite->new("127.0.0.0/8"); # TODO: add IPv6 local IP
447 0           my $trustedIP =
448             Net::CIDR::Lite->new( split /\s+/, $self->{trustedProxies} );
449 0   0       while (
      0        
450             (
451             $localIP->find( $self->{ipAddr} )
452             or $trustedIP->find( $self->{ipAddr} )
453             )
454             and $xheader =~ s/[,\s]*([^,\s]+)$//
455             )
456             {
457              
458             # because it is of no use to store a local IP as client IP
459 0 0         $self->{ipAddr} = $1 unless ( $localIP->find($1) );
460             }
461             }
462             }
463             }
464 0           return $self->{ipAddr};
465             }
466              
467             1;
468              
469             __END__