File Coverage

blib/lib/Cake/Engine.pm
Criterion Covered Total %
statement 44 285 15.4
branch 6 84 7.1
condition 5 47 10.6
subroutine 13 35 37.1
pod 0 22 0.0
total 68 473 14.3


line stmt bran cond sub pod time code
1             package Cake::Engine;
2 8     8   58 use strict;
  8         21  
  8         335  
3 8     8   196 use warnings;
  8         17  
  8         244  
4 8     8   43 use Carp;
  8         28  
  8         600  
5 8     8   7597 use IO::File;
  8         102083  
  8         1247  
6 8     8   10115 use File::Temp qw/ tempfile tempdir /;
  8         123100  
  8         4767  
7            
8             sub init {
9 2     2 0 18 my $self = shift;
10 2   50     20 my $uri = $self->env->{'REQUEST_URI'} || '';
11 2         17 my($path, $query) = ( $uri =~ /^([^?]*)(?:\?(.*))?$/s );
12            
13             ##remove script name from path info
14 2   50     10 my $script = $self->env->{SCRIPT_NAME} || '';
15 2         32 $path =~ s/^$script//;
16            
17             #for ($path, $query) { s/\#.*$// if length } # dumb clients sending URI fragments
18 2         19 $self->env->{PATH_INFO} = Cake::URI::uri_decode($path);
19 2   50     18 $self->env->{QUERY_STRING} = $query || '';
20 2         14 $self->engine(bless {}, __PACKAGE__);
21             }
22            
23             #BEGIN { open (STDERR, ">>/xampp/htdocs/CakeBlog/error.txt"); }
24            
25             #=============================================================================
26             # finalize output
27             #=============================================================================
28             sub finalize {
29 3     3 0 14 my $self = shift;
30 3         45 $self->printConsole();
31 3         7 return $self;
32             }
33            
34             #=============================================================================
35             # console debugging
36             #=============================================================================
37             sub printConsole {
38 3     3 0 5 my $self = shift;
39 3 50       15 return if !$self->debug;
40 0 0       0 if (my $logs = $self->app->{log}){
41            
42 0         0 my $debug = "\n=======================\n";
43 0         0 $debug .= " DEBUGGING CONSOLE ||\n";
44 0         0 $debug .= _charFormatter('=');
45 0         0 $debug .= "REQUEST PATH : ".$self->path . "\n";
46 0         0 $debug .= "REQUEST METHOD : ".$self->method . "\n";
47            
48             ##log request params
49            
50 0         0 $debug .= _charFormatter('#');
51 0         0 $debug .= _charFormatter(' ');
52            
53 0         0 my $i = 0;
54 0         0 foreach my $log (@{$logs}){
  0         0  
55            
56 0 0       0 if (ref $log eq "ARRAY"){
    0          
57 0         0 $log = join "\n",@{$log};
  0         0  
58             } elsif (ref $log eq 'CODE') {
59 0         0 $log = $log->();
60             }
61            
62 0 0       0 if (length($log) > 65){
63 0         0 my @logs = unpack("(A62)*", $log);
64 0         0 my $last = pop @logs;
65 0         0 foreach my $lo (@logs){
66 0         0 $lo .= "->";
67 0         0 $debug .= _printFormatter($lo);
68             }
69 0         0 $log = $last;
70             }
71            
72 0         0 $debug .= _printFormatter($log);
73 0         0 $debug .= _charFormatter(' ');
74             }
75 0         0 $debug .= _charFormatter('#');
76 0         0 warn $debug."\n";
77             }
78            
79 0 0       0 if (my $warnings = delete $self->app->{warnings}){
80 8     8   80 use Data::Dumper;
  8         16  
  8         3658  
81 0         0 my $warn = "\n=======================\n";
82 0         0 $warn .= " WARNING CONSOLE ||\n";
83 0         0 $warn .= _charFormatter('=');
84 0         0 my $count = 0;
85 0         0 foreach my $key (keys %{$warnings}){
  0         0  
86 0         0 $warn .= _charFormatter('-');
87 0         0 $warn .= "CALLER => ". $key . "\n";
88 0         0 $warn .= "WARNS => ". scalar @{ $warnings->{$key} } . "\n";
  0         0  
89 0         0 $warn .= _charFormatter('-');
90 0         0 for (@{ $warnings->{$key} }){
  0         0  
91 0         0 $count++;
92 0         0 $warn .= "Message: ". $_->{message} ."\n";
93 0         0 $warn .= "Line: ". $_->{line} ."\n\n";
94             }
95            
96 0         0 $warn .= "\n";
97             }
98            
99 0         0 $warn .= _charFormatter('#');
100 0         0 my $str = "# TOTAL WARNINGS : $count ";
101 0         0 $warn .= _printFormatter($str);
102 0         0 $warn .= _charFormatter('#');
103            
104 0         0 warn $warn;
105             }
106 0         0 $self->app->{log} = [];
107             }
108            
109             sub _printFormatter {
110 0     0   0 my $text = shift;
111 0   0     0 my $padd = shift || ' ';
112 0         0 my $form .= "$text";
113 0         0 $form .= $padd x (64 - length($text)) . "#\n";
114 0         0 return $form;
115             }
116            
117             sub _charFormatter {
118 0     0   0 my $char = shift;
119 0   0     0 my $multi = shift || 64;
120 0         0 return ($char x $multi) . "#\n";
121             }
122            
123             #=============================================================================
124             # serve output
125             #=============================================================================
126             sub serve {
127            
128 3     3 0 6 my $self = shift;
129 3         6 my $type = shift;
130            
131 3 50       10 if ($type){
132 3 50       49 return $self->serve_as_psgi if uc $type eq 'PSGI';
133 3 50       23 if (ref $type eq 'CODE'){
134 3         12 return $type->($self,$self->env->{client});
135             }
136 0         0 $self->Require($type,'');
137            
138             {
139 8     8   56 no strict 'refs';
  8         19  
  8         20921  
  0         0  
140 0         0 *{"${type}::serve"}->($self);
  0         0  
141             }
142             }
143            
144             else {
145 0         0 $self->print_headers();
146 0         0 $self->print_body();
147             }
148            
149 0         0 close $self->{response}->{body};
150             }
151            
152            
153            
154             #=============================================================================
155             # get parameters
156             #=============================================================================
157             sub get_parameters {
158 0     0 0 0 my $self = shift;
159 0         0 return $self->_parse_parameters($self->env->{'QUERY_STRING'});
160             }
161            
162             #=============================================================================
163             # body parameters
164             #=============================================================================
165             sub post_parameters {
166 0     0 0 0 my $self = shift;
167 0         0 my $buffer;
168            
169 0 0       0 return {} if $self->method ne 'post';
170            
171 0 0 0     0 if (my $tt = ($self->env->{'client.input'} || $self->env->{'psgi.input'})){
172             {
173 0         0 local $/;
  0         0  
174 0         0 $buffer = <$tt>;
175             }
176             }
177             ##CGI
178             else {
179 0         0 read( STDIN, $buffer, $self->env->{ "CONTENT_LENGTH" } );
180             }
181            
182 0 0       0 if ($self->env->{CONTENT_TYPE} =~ /^multipart\/form-data/i){
183 0         0 $self->env->{CONTENT_TYPE} =~ m/boundary=(.*)/;
184 0         0 my $boundary = $1;
185 0         0 return $self->_multipart_parameters($buffer,$boundary);
186             }
187            
188 0         0 return $self->_parse_parameters($buffer);
189             }
190            
191             #=============================================================================
192             # process multipart body parameters
193             #=============================================================================
194             sub _multipart_parameters {
195            
196 0     0   0 my $self = shift;
197 0         0 my $params = shift;
198 0         0 my $boundary = shift;
199            
200 0         0 my $CRLF = $self->crlf;
201 0         0 $boundary = '--'.$boundary;
202 0         0 $params =~ s/$CRLF$boundary--//g;
203            
204 0         0 my @params = split($boundary.$CRLF,$params);
205            
206 0         0 my @query;
207             my $handle;
208            
209 0         0 for my $field (@params){
210            
211             ##remove first line
212 0         0 $field =~ s/^$CRLF//;
213            
214             ##remove last blank
215 0         0 $field =~ s/$CRLF$//;
216            
217             ##split on first 2 line breaks
218 0         0 my ($header,$content) = split(/$CRLF$CRLF/,$field,2);
219 0 0       0 next if !$header;
220            
221             ##split header on line break
222 0         0 my ($name,$filename,$contenttype) =
223             $header =~ m/
224             name="?([^\";]*)"?
225             (?:;\s+filename="?([^\"]*)"?$CRLF)?
226             (?:Content-Type:(.*))?
227             /x;
228            
229            
230 0 0       0 if($filename){
231             #make sure the file name is safe
232 0         0 my $dir = tempdir( CLEANUP => 1 );
233 0         0 my ($fh, $tmp) = tempfile(
234             DIR => $dir,
235             SUFFIX => '.dat'
236             );
237            
238 0         0 $fh->unlink_on_destroy( 1 );
239            
240 0 0       0 if (defined $fh) {
241 0         0 binmode $fh, ":utf8";
242 0         0 print $fh $content;
243 0         0 $fh->close;
244             }
245            
246 0         0 $self->{uploads}->{$name} = {
247             'filehandle' => $fh,
248             'filename' => $filename,
249             temp => $tmp,
250             'path' => $dir,
251             'content-type' => $contenttype
252             };
253            
254 0         0 $content = $filename;
255             }
256            
257 0         0 push(@query,$name.'='.Cake::URI::uri_encode($content));
258             }
259            
260 0         0 my $query = join('&',@query);
261 0         0 return $self->_parse_parameters($query);
262             }
263            
264             #=============================================================================
265             # XXX - TODO return uploads info & file handle
266             #=============================================================================
267             sub uploads {
268            
269 0     0 0 0 my $self = shift;
270            
271             #didn't parse params yet
272 0 0       0 unless (defined $self->{'params'}){
273 0         0 $self->parameters();
274             }
275            
276 0 0       0 return {} if !$self->{uploads};
277 0         0 return $self->{uploads};
278             }
279            
280             sub upload {
281 0     0 0 0 my $self = shift;
282 0         0 my $name = shift;
283 0         0 return $self->uploads->{$name};
284             }
285            
286            
287             #=============================================================================
288             # return processed parameters
289             #=============================================================================
290             sub parameters {
291 0     0 0 0 my $self = shift;
292 0 0       0 return $self->{'params'} if $self->{'params'};
293 0         0 my $params = Cake::Utils::combineHashes($self->get_parameters,$self->post_parameters);
294 0         0 $self->{'params'} = $params;
295 0         0 return $params;
296             }
297            
298             #=============================================================================
299             # parse parameters
300             #=============================================================================
301             sub _parse_parameters {
302            
303 0     0   0 my $self = shift;
304 0         0 my $content = shift;
305 0         0 my $params = {};
306            
307 0         0 my @pairs = split(/[&;]/, $content);
308 0         0 foreach my $pair (@pairs) {
309 0         0 my ($name, $value) = map Cake::URI::uri_decode($_), split( "=", $pair, 2 );
310            
311 0 0       0 if ($name =~ s/\[(\d+)\]\[(.*?)\]//){
312 0         0 my $index = $1;
313 0         0 my $n = $2;
314 0 0       0 if (!ref $params->{$name}){
315 0         0 $params->{$name} = [];
316             }
317 0         0 $params->{$name}->[$index]->{$n} = $value;
318             } else {
319            
320 0 0 0     0 if ($name =~ s/\[\]$// && !ref $params->{$name}){
321 0         0 $params->{$name} = [];
322             }
323            
324 0 0       0 if ($params->{$name}){
325 0 0       0 if (!ref $params->{$name}){
326 0         0 my $holds = [$params->{$name},$value];
327 0         0 $params->{$name} = $holds;
328             } else {
329 0         0 push (@{$params->{$name}},$value);
  0         0  
330             }
331             } else {
332 0         0 $params->{$name} = $value;
333             }
334             }
335             }
336            
337 0         0 return $params;
338             }
339            
340             #serve content as psgi
341             sub serve_as_psgi {
342            
343 0     0 0 0 my $self = shift;
344            
345 0         0 my @headers = $self->_get_psgi_headers;
346            
347 0         0 my $body = $self->body();
348            
349 0 0       0 seek($body,0,0) if ref $body eq 'GLOB';
350            
351 0 0       0 if (!ref $body){
    0          
352 0         0 $body = [ $body ];
353             }
354            
355             elsif (ref $body eq 'CODE'){
356            
357             return sub {
358 0     0   0 my $response = shift;
359 0         0 my $w = $response->([ $self->status_code(), \@headers ]);
360 0         0 $body->($w);
361 0         0 };
362            
363 0         0 return $body;
364             }
365            
366 0         0 return [ $self->status_code, \@headers, $body ];
367             }
368            
369             sub _get_psgi_headers {
370 0     0   0 my $self = shift;
371 0         0 my @headers = ('Content-Type',$self->content_type);
372 0         0 foreach my $header (@{$self->headers}){
  0         0  
373 0         0 my @nh = split(/:/,$header,2);
374 0         0 push (@headers,@nh);
375             }
376 0         0 return @headers;
377             }
378            
379             sub print_headers {
380 0     0 0 0 my $self = shift;
381 0         0 my $headers;
382            
383             ##normal print/ for CGI
384 0         0 my $content_type_header = 'Content-Type: '.$self->content_type;
385 0         0 my $status_code = 'Status-code: '.$self->status_code;
386 0         0 $headers = Cake::Utils::get_status_code($self->env,$self->status_code);
387 0         0 $headers .= "$content_type_header\015\012";
388 0         0 my $found_content_length;
389 0         0 foreach my $header (@{$self->headers}){
  0         0  
390 0         0 $headers .= $header."\015\012";
391 0 0 0     0 $found_content_length = 1
392             if $header =~ /^Content-Length/i && !$found_content_length;
393             }
394            
395 0 0       0 unless ($found_content_length){
396 0         0 my $body = $self->body();
397 0 0       0 $headers .= "Content-Length: ".Cake::Utils::content_length($body)."\015\012" if $body;
398             }
399            
400 0         0 $headers .= "\015\012";
401 0         0 my $stdout = $self->stdout;
402 0         0 print $stdout $headers;
403             }
404            
405             sub print_body {
406 0     0 0 0 my $self = shift;
407 0         0 my $body = $self->body();
408 0         0 my $stdout = $self->stdout;
409 0         0 binmode $stdout;
410 0 0       0 if (ref $body eq 'GLOB'){
    0          
411             ##seek to the start
412 0         0 seek($body,0,0);
413 0         0 local $/ = undef;
414 0         0 $body = <$body>;
415 0         0 print $stdout $body;
416             }
417            
418             elsif (ref $body eq 'CODE'){
419 0         0 $body->(__PACKAGE__);
420             }
421             }
422            
423             #=============================================================================
424             # ENV
425             #=============================================================================
426             sub path {
427 4 50   4 0 29 if (@_ > 1){
428 0         0 $_[0]->env->{PATH_INFO} = $_[1];
429 0         0 return $_[0];
430             }
431 4   50     18 return $_[0]->env->{PATH_INFO} || '/';
432             }
433            
434             sub method {
435 3 50   3 0 14 if (@_ > 1){
436 0         0 $_[0]->env->{REQUEST_METHOD} = $_[1];
437             }
438 3   50     14 return lc ($_[0]->env->{REQUEST_METHOD} || '');
439             }
440            
441             sub is_secure {
442 0 0   0 0   return $_[0]->env->{'SSL_PROTOCOL'} ? 1 : 0;
443             }
444            
445             sub base {
446 0     0 0   my $self = shift;
447 0           my $base = 'http';
448 0 0         $base .='s' if $self->is_secure();
449 0           $base .= '://'.$self->env->{HTTP_HOST};
450 0           return $base;
451             }
452            
453             sub host {
454 0     0 0   return $_[0]->env->{HTTP_HOST};
455             }
456            
457             sub server_protocol {
458 0   0 0 0   return shift->env->{SERVER_PROTOCOL} || 'HTTP/1.1';
459             }
460            
461             sub request_header {
462            
463 0     0 0   my $self = shift;
464 0           my $header = uc shift;
465 0           my $response_headers = {
466             'ETAG' => 'IF-NONE-MATCH'
467             };
468 0           $header =~ s/^(HTTP[-_])//;
469 0           $header =~ s/[-\s]/_/g;
470 0   0       $header = $response_headers->{$header} || $header;
471 0           $header = 'HTTP_'.$header;
472 0           return $self->env->{$header};
473             }
474            
475             sub stdout {
476 0     0 0   my $self = shift;
477 0 0         if (@_){
478 0           $self->env->{client} = shift;
479             }
480 0   0       return $self->env->{client} || \*STDOUT;
481             }
482            
483             #=============================================================================
484             # cookies : return cookies list as hash - copied from plack
485             #=============================================================================
486             sub cookies {
487 0     0 0   my $self = shift;
488 0 0         return {} unless $self->env->{HTTP_COOKIE};
489            
490             # HTTP_COOKIE hasn't changed: reuse the parsed cookie
491 0 0 0       if ( $self->env->{'cake.cookie.parsed'}
492             && $self->env->{'cake.cookie.string'} eq $self->env->{HTTP_COOKIE}) {
493 0           return $self->env->{'cake.cookie.parsed'};
494             }
495            
496 0           $self->env->{'cake.cookie.string'} = $self->env->{HTTP_COOKIE};
497            
498 0           my %results;
499 0           my @pairs = grep /=/, split "[;,] ?", $self->env->{'cake.cookie.string'};
500            
501 0           for my $pair ( @pairs ) {
502             # trim leading trailing whitespace
503 0           $pair =~ s/^\s+//; $pair =~ s/\s+$//;
  0            
504             #my ($key, $value) = split( "=", $pair, 2 );
505 0           my ($key, $value) = map Cake::URI::uri_decode($_), split( "=", $pair, 2 );
506             # Take the first one like CGI.pm or rack do
507 0 0         $results{$key} = $value unless exists $results{$key};
508             }
509            
510 0           $self->env->{'cake.cookie.parsed'} = \%results;
511 0           return \%results;
512             }
513            
514             #=============================================================================
515             # set/get a cookie
516             #=============================================================================
517             sub cookie {
518 0     0 0   my $self = shift;
519            
520 0 0         if (ref $_[0] eq 'HASH'){
521 0           my $args = shift;
522 0   0       my $name = Cake::URI::uri_encode($args->{name} || ref $self->app);
523 0   0       my $value = Cake::URI::uri_encode($args->{value} || '');
524 0   0       my $secure = $args->{secure} || '0';
525 0   0       my $path = $args->{path} || '/';
526 0           my $time = '';
527            
528 0 0         if ($args->{length}){
529 0           my $length = Cake::Utils::to_epoch($args->{length});
530 0           $time = gmtime($length)." GMT";
531             }
532            
533 0           my $cookie = "$name=$value; path=$path; expires=$time; $secure";
534 0           $self->push_header('Set-Cookie: '.$cookie);
535             }
536            
537 0 0 0       croak 'cookie method only accepts Hash ref for setting and string for getting'
538             if ref $_[0] || @_ > 1;
539            
540 0   0       my $name = shift || '';
541 0           return $self->cookies->{$name};
542             }
543            
544             1;