File Coverage

blib/lib/Waft.pm
Criterion Covered Total %
statement 630 954 66.0
branch 194 398 48.7
condition 67 166 40.3
subroutine 117 157 74.5
pod 30 97 30.9
total 1038 1772 58.5


line stmt bran cond sub pod time code
1             package Waft;
2              
3 23     23   242211 use 5.005;
  23         95  
  23         6093  
4 23     23   151 use strict;
  23         49  
  23         1633  
5 23     23   126 use vars qw( $VERSION );
  23         56  
  23         2174  
6 23 50   23   58 BEGIN { eval { require warnings } ? 'warnings'->import : ( $^W = 1 ) }
  23         1244  
7              
8 23     23   114569 use CGI qw( -no_debug );
  23         633896  
  23         203  
9 23     23   2744 use Fcntl qw( :DEFAULT );
  23         269  
  23         21709  
10 23     23   33842 use Symbol;
  23         39049  
  23         7555  
11             require File::Spec;
12              
13             $VERSION = '0.99_90';
14             $VERSION = eval $VERSION;
15              
16             $Waft::Backword_compatible_version = $VERSION < 1.0 ? 1.0 : $VERSION;
17             @Waft::Allow_template_file_exts = qw( .html .css .js .txt );
18             $Waft::Cache = 1;
19             $Waft::Correct_NEXT_DISTINCT = 1;
20              
21             sub import {
22 2     2   25 my ($class, @mixins) = @_;
23              
24 2 50 33     26 if ( defined $mixins[0] and $mixins[0] eq 'with' ) {
25 2         37 shift @mixins;
26             }
27              
28 2 50       10 return if @mixins == 0;
29              
30 2         5 my $caller = caller;
31 2         4 my @bases;
32              
33             BASE:
34 2         6 for my $base ( @mixins, $class ) {
35 8 100       1375 if ( $base =~ /\A :: /xms ) {
36 4         12 $base = 'Waft' . $base;
37             }
38              
39 8 50       97 next BASE if $caller->isa($base);
40              
41 8         472 eval qq{ require $base };
42              
43 8 100       1734 if ( $@ ) {
44 1 50       16 CORE::die($@) if $@ !~ /\ACan't locate .*? at \(eval /;
45              
46 23 50   23   427 if ( not do { no strict 'refs'; %{ "${base}::" } } ) {
  23         50  
  23         2485  
  1         4  
  1         2  
  1         20  
47 0         0 require Carp;
48              
49 0         0 Carp::croak($@);
50             }
51             }
52              
53 8         29 push @bases, $base;
54             }
55              
56 23     23   124 no strict 'refs';
  23         52  
  23         31063  
57 2         6 push @{ "${caller}::ISA" }, @bases;
  2         224  
58              
59 2         11210 return;
60             }
61              
62             {
63             my %Backword_compatible_version_of;
64              
65             sub set_waft_backword_compatible_version {
66 2     2 1 20 my ($class, $backword_compatible_version) = @_;
67              
68 2 50       19 $class->die('This is class method') if $class->blessed;
69              
70 2         6 $Backword_compatible_version_of{$class}
71             = $backword_compatible_version;
72              
73 2         6 return;
74             }
75              
76             sub BCV {
77 149     149 0 222 my ($self) = @_;
78              
79 149   66     707 my $class = $self->blessed || $self;
80              
81 149   66     500 my $backword_compatible_version
82             = $Backword_compatible_version_of{$class}
83             || $Waft::Backword_compatible_version;
84              
85 149         710 return $backword_compatible_version;
86             }
87             }
88              
89 0     0 0 0 sub get_waft_backword_compatible_version { shift->BCV(@_) }
90              
91 23     23   154 eval q{ use Scalar::Util qw( blessed refaddr ); 1 } or do {
  23         314  
  23         3555  
92             *blessed = *blessed = sub {
93             my ($self) = @_;
94              
95             my $blessed = ref $self;
96              
97             return $blessed;
98             };
99              
100             *refaddr = *refaddr = sub {
101             my ($self) = @_;
102              
103             my $blessed_class = ref $self
104             or return;
105              
106             bless $self, __PACKAGE__;
107             my $refaddr = "$self";
108              
109             bless $self, $blessed_class;
110              
111             return $refaddr;
112             };
113             };
114              
115             sub die {
116 2     2 1 41110 my ($self, @args) = @_;
117              
118 2 50   0   9 $self->dont_trust_me( sub { CORE::die(@_) }, @args ) if $self->BCV < 1.0;
  0         0  
119              
120 2 100   1   21 $self->dont_trust_me( sub { CORE::die(q{Error: }, @_) }, @args )
  1         9  
121             if not defined wantarray;
122              
123 1     1   16 $self->dont_trust_me( sub { CORE::warn(q{Error: }, @_) }, @args );
  1         80  
124              
125 1 50       18 return 'internal_server_error', @args if not $self->responded;
126 0         0 return @args;
127             }
128              
129             sub dont_trust_me {
130 7     7 0 22 my ($self, $coderef, @args) = @_;
131              
132 7   66     39 my $class = $self->blessed || $self;
133              
134 7         10 my $back;
135             CALLER:
136 7         91 while ( my @caller = caller $back++ ) {
137 14         32 my ($package, $filename, $line) = @caller;
138              
139 14 100 66     147 next CALLER if $package ne $class and $self->isa($package);
140              
141 7 100       22 if ( not grep { defined and length >= 1 } @args ) {
  8 100       62  
142 3         7 push @args, q{something's wrong};
143             }
144              
145 7         29 push @args, " at $filename line $line.\n";
146              
147 7         23 last CALLER;
148             }
149              
150 7         25 return $coderef->(@args);
151             }
152              
153             sub use_utf8 {
154 1     1 1 1157 my ($class) = @_;
155              
156 1         9 $class->set_using_utf8(1);
157              
158 1         2 return;
159             }
160              
161             {
162             my %Using_utf8;
163              
164             sub set_using_utf8 {
165 1     1 0 4 my ($class, $using_utf8) = @_;
166              
167 1 50       12 $class->die('This is class method') if $class->blessed;
168              
169 1 50 33     14 return if $using_utf8 and not $class->can_use_utf8;
170              
171 1         4 $Using_utf8{$class} = $using_utf8;
172              
173 1         2 return;
174             }
175              
176             sub get_using_utf8 {
177 14     14 0 27 my ($self) = @_;
178              
179 14 50       44 if ($self->BCV < 0.53) {
180 0 0       0 return $self->stash->{use_utf8} if $self->blessed;
181             }
182              
183 14   33     64 my $class = $self->blessed || $self;
184              
185 14         28 my $using_utf8 = $Using_utf8{$class};
186              
187 14         71 return $using_utf8;
188             }
189             }
190              
191             sub can_use_utf8 {
192 1     1 0 2 my ($self) = @_;
193              
194 1         3 eval { require 5.008001 };
  1         20  
195 1 50       12 return 1 if not $@;
196 0         0 $self->warn($@);
197              
198 0         0 return;
199             }
200              
201             sub warn {
202 5     5 1 3753 my ($self, @args) = @_;
203              
204 5 50       20 if ($self->BCV < 1.0) {
205 0     0   0 $self->dont_trust_me( sub { CORE::warn(@_) }, @args );
  0         0  
206              
207 0         0 return;
208             }
209              
210 5     5   39 $self->dont_trust_me( sub { CORE::warn(q{Warning: }, @_) }, @args );
  5         263  
211              
212 5         30 return;
213             }
214              
215             {
216             my %Allow_template_file_exts_arrayref_of;
217              
218             sub set_allow_template_file_exts {
219 4     4 1 1475 my ($class, @allow_template_file_exts) = @_;
220              
221 4 50       20 $class->die('This is class method') if $class->blessed;
222              
223 4         8 $Allow_template_file_exts_arrayref_of{$class}
224             = \@allow_template_file_exts;
225              
226 4         12 return;
227             }
228              
229             sub get_allow_template_file_exts {
230 41   33 41 0 106 my $class = $_[1] || $_[0];
231              
232 41 100       102 return @{ $Allow_template_file_exts_arrayref_of{$class} }
  31         103  
233             if exists $Allow_template_file_exts_arrayref_of{$class};
234              
235 10         15 my $get_allowed_exts = do {
236 23     23   438 no strict 'refs';
  23         330  
  23         128399  
237 10         14 *{ "${class}::allow_template_file_exts" }{CODE};
  10         60  
238             };
239              
240             my @allow_template_file_exts
241 10 100       52 = $get_allowed_exts ? $get_allowed_exts->($class)
242             : @Waft::Allow_template_file_exts;
243              
244 10         33 $Allow_template_file_exts_arrayref_of{$class}
245             = \@allow_template_file_exts;
246              
247 10         36 return @allow_template_file_exts;
248             }
249             }
250              
251             {
252             my %Default_content_type_of;
253              
254             sub set_default_content_type {
255 0     0 1 0 my ($class, $default_content_type) = @_;
256              
257 0 0       0 $class->die('This is class method') if $class->blessed;
258              
259 0         0 $Default_content_type_of{$class} = $default_content_type;
260              
261 0         0 return;
262             }
263              
264             sub get_default_content_type {
265 1     1 0 3 my ($self) = @_;
266              
267 1   33     8 my $class = $self->blessed || $self;
268              
269 1   50     7 my $default_content_type = $Default_content_type_of{$class}
270             || 'text/html';
271              
272 1         2 return $default_content_type;
273             }
274             }
275              
276             sub waft {
277 0     0 1 0 my ($self, @args) = @_;
278              
279 0 0       0 if ($self->BCV < 0.53) {
280 0 0       0 if ( not $self->blessed ) {
281 0         0 ($self, @args) = $self->new(@args);
282             }
283              
284 0         0 $self->init_base_url;
285 0         0 $self->init_binmode;
286 0         0 $self->_load_query_param;
287             }
288              
289 0 0       0 if ( not $self->blessed ) {
290 0         0 $self = $self->new->initialize;
291             }
292              
293 0         0 my @return_values = $self->controller(@args);
294              
295 0 0       0 return wantarray ? ($self, @return_values) : $self;
296             }
297              
298             sub new {
299 18     18 1 3904 my ($class) = @_;
300              
301 18 50       201 $class->die('This is class method') if $class->blessed;
302              
303 18         39 my $self;
304 18         132 tie %$self, 'Waft::Object';
305 18         48 bless $self, $class;
306              
307 18 100       147 if ($class->BCV < 1.0) {
308 2         15 $class->define_subs_for_under_0_99x;
309             }
310              
311 18 50       63 if ($class->BCV < 0.53) {
312 0         0 ( undef, my @args ) = @_;
313              
314 0         0 $class->define_subs_for_under_0_52x;
315              
316 0         0 my $self;
317 0         0 tie %$self, 'Waft::Object';
318 0         0 bless $self, $class;
319              
320 0         0 my ($option_hashref, @return_values);
321              
322 0 0       0 if (ref $args[0] eq 'HASH') {
323 0         0 ($option_hashref, @return_values) = @args;
324             }
325             else {
326 0         0 $option_hashref = { @args };
327             }
328              
329 0   0     0 $option_hashref->{content_type} ||= $self->get_default_content_type;
330 0   0     0 $option_hashref->{headers} ||= [];
331              
332 0         0 my $stash = $self->stash;
333              
334 0         0 %$stash = %$option_hashref;
335              
336 0 0       0 if ($stash->{use_utf8}) {
337 0         0 $self->can_use_utf8; # carp in this method if cannot 'use utf8'
338             }
339              
340 0 0       0 return wantarray ? ($self, @return_values) : $self;
341             }
342              
343 18         89 return $self;
344             }
345              
346             sub initialize {
347 2     2 1 11 my ($self) = @_;
348              
349 2         13 $self->initialize_base_url;
350 2         15 $self->initialize_page;
351 2         14 $self->initialize_values;
352 2         17 $self->initialize_action;
353 2         10 $self->initialize_response_headers;
354 2         13 $self->initialize_binmode;
355              
356 2         6 return $self;
357             }
358              
359             sub initialize_base_url {
360 3     3 0 17 my ($self) = @_;
361              
362 3         20 my $base_url = $self->make_base_url;
363 3         23 $self->set_base_url($base_url);
364              
365 3         8 return;
366             }
367              
368             sub make_base_url {
369 3     3 0 6 my ($self) = @_;
370              
371 3   50     46 my $updir = $ENV{PATH_INFO} || q{};
372 3         8 my $updir_count = $updir =~ s{ /[^/]* }{../}gx;
373              
374 3         8 my $url;
375              
376 3 50 33     19 if ( defined $ENV{REQUEST_URI}
377             and $ENV{REQUEST_URI} =~ /\A ([^?]+) /xms
378             ) {
379 0         0 $url = $1;
380              
381 0         0 for (1 .. $updir_count) {
382 0         0 $url =~ s{ /[^/]* \z}{}x;
383             }
384             }
385             else {
386 3   66     19 $url = $ENV{SCRIPT_NAME} || $self->get_script_basename;
387             }
388              
389 3 50       38 my $base_url = $url =~ m{ ([^/]+) \z}xms ? "$updir$1"
390             : './';
391              
392 3         12 return $base_url;
393             }
394              
395             sub get_script_basename {
396 3     3 0 529 my ($self) = @_;
397              
398 3 100       5 return $FindBin::Script if eval { FindBin::again(); 1 };
  3         49  
  1         211  
399              
400 2         8 delete $INC{'FindBin.pm'};
401 2         2114 require FindBin;
402              
403 2         2604 return $FindBin::Script;
404             }
405              
406             sub set_base_url {
407 3     3 0 8 my ($self, $base_url) = @_;
408              
409 3 50       11 if ($self->BCV < 0.53) {
410 0         0 $self->stash->{url} = $base_url;
411             }
412              
413 3         15 $self->stash->{base_url} = $base_url;
414              
415 3         7 return;
416             }
417              
418             {
419             my %Stash;
420              
421 108   33 108 1 1337 sub stash { $Stash{ $_[0]->refaddr or $_[0] }{ $_[1] or caller } ||= {} }
      33        
      100        
422              
423             sub DESTROY {
424 18     18   10343 my ($self) = @_;
425              
426 18         120 my $ident = $self->refaddr;
427 18         85 delete $Stash{$ident};
428              
429 18         1483 return;
430             }
431             }
432              
433             sub initialize_page {
434 5     5 0 247 my ($self) = @_;
435              
436 5 100       32 my $page = $self->is_submitted ? $self->cgi->param('s')
437             : $self->cgi->param('p');
438              
439 5 100 100     14017 if ( $self->get_using_utf8 and defined $page ) {
440 1         6 utf8::encode($page);
441             }
442              
443 5         39 $page = $self->fix_and_validate_page($page);
444 5 100       45 $self->set_page( defined $page ? $page : 'default.html' );
445              
446 5         23 return;
447             }
448              
449             sub is_submitted {
450 8     8 0 27 my ($self) = @_;
451              
452 8         35 my $is_submitted = defined $self->cgi->param('s');
453              
454 8         188 return $is_submitted;
455             }
456              
457             sub cgi {
458 25     25 1 41 my ($self) = @_;
459              
460 25   66     67 my $query = ( $self->stash->{query} ||= $self->create_query_obj );
461              
462 25         9628 return $query;
463             }
464              
465             sub create_query_obj {
466 4     4 0 8 my ($self) = @_;
467              
468 4         33 my $query = CGI->new;
469              
470 4 100       14446 if ($self->get_using_utf8) {
471 2         98 eval qq{\n# line } . __LINE__ . q{ "} . __FILE__ . qq{"\n} . q{
472 1     1   8 use CGI 3.21 qw( -utf8 ); # -utf8 pragma is for 3.31 or later
  1     1   24  
  1         8  
  1         6  
  1         16  
  1         7  
473             };
474              
475 2 50       131 if ($@) {
    50          
476 0         0 $self->warn($@);
477             }
478             elsif ($query->VERSION < 3.31) {
479 0         0 $query->charset('utf-8');
480             }
481             }
482              
483 4         29 return $query;
484             }
485              
486             sub fix_and_validate_page {
487 5     5 0 16 my ($self, $page) = @_;
488              
489 5 100       25 return if not defined $page;
490              
491 3         33 $page =~ m{\A
492             (?! .* [/\\]{2,} )
493             (?! .* (?
494             (?! .* :: )
495             (.+) \z}xms;
496 3         11 my $untainted_page = $1;
497              
498 3 50 33     109 return $untainted_page
      33        
      33        
      33        
499             if defined $untainted_page
500             and not File::Spec->file_name_is_absolute($untainted_page)
501             and not $untainted_page eq 'CURRENT'
502             and not $untainted_page eq 'TEMPLATE'
503             and not $self->to_page_id($untainted_page) =~ / __indirect \z/xms;
504              
505 0         0 $self->warn(qq{Invalid requested page "$page"});
506              
507 0         0 return;
508             }
509              
510             sub to_page_id {
511 5     5 0 15 my (undef, $page) = @_;
512              
513 5         9 my $page_id = $page;
514 5         30 $page_id =~ s{ \.[^/:\\]* \z}{}xms;
515 5         14 $page_id =~ tr/0-9A-Za-z_/_/c;
516              
517 5         25 return $page_id;
518             }
519              
520             sub set_page {
521 5     5 0 11 my ($self, $page) = @_;
522              
523 5         18 $self->stash->{page} = $page;
524              
525 5         19 return;
526             }
527              
528             sub initialize_values {
529 4     4 0 10 my ($self, $joined_values) = @_;
530              
531 4         24 $self->clear_values;
532              
533 4   100     22 $joined_values ||= $self->cgi->param('v');
534              
535 4 100       96 return if not defined $joined_values;
536              
537 3         14 my @key_values_pairs = split /\x20/, $joined_values, -1;
538              
539             KEY_VALUES_PAIR:
540 3         9 for my $key_values_pair (@key_values_pairs) {
541 6         19 my ($key, @values) = split /-/, $key_values_pair, -1;
542              
543 6         29 $key = $self->unescape_space_percent_hyphen($key);
544 6         20 @values = $self->unescape_space_percent_hyphen(@values);
545              
546 6 50       31 if ($key eq 'ALL_VALUES') {
547 0         0 $self->warn(q{Invalid init value 'ALL_VALUES'});
548              
549 0         0 next KEY_VALUES_PAIR;
550             }
551              
552 6         23 $self->set_values( $key => @values );
553             }
554              
555 3         8 return;
556             }
557              
558             sub clear_values {
559 6     6 1 21 my ($self) = @_;
560              
561 6         13 %{ $self->value_hashref } = ();
  6         32  
562              
563 6         15 return;
564             }
565              
566 42     42 0 52 sub value_hashref { tied %{ $_[0] } }
  42         209  
567              
568             sub unescape_space_percent_hyphen {
569 12     12 0 25 my (undef, @values) = @_;
570              
571 12         17 for my $value (@values) {
572 12         30 $value =~ s/ %(2[05d]) / pack 'H2', $1 /egxms;
  6         24  
573             }
574              
575 12 100       46 return wantarray ? @values : $values[0];
576             }
577              
578             sub set_values {
579 9     9 1 31 my ($self, $key, @values) = @_;
580              
581 9         22 @{ $self->value_hashref->{$key} } = @values;
  9         29  
582              
583 9         28 return;
584             }
585              
586             sub initialize_action {
587 3     3 0 8 my ($self) = @_;
588              
589 3         21 my $action = $self->find_first_action;
590 3 100       26 $self->set_action( defined $action ? $action : 'direct' );
591              
592 3         6 return;
593             }
594              
595             sub find_first_action {
596 3     3 0 38 my ($self) = @_;
597              
598 3 100       9 return if not $self->is_submitted;
599              
600 2         16 my $page_id = $self->to_page_id($self->get_page);
601 2         14 my $global_action;
602              
603 2         9 my @param_names = $self->cgi->param;
604             PARAM_NAME:
605 2         36 for my $param_name ( @param_names ) {
606 6         53 my $action_id = $self->to_action_id($param_name);
607              
608 6 50       15 if ($self->BCV < 0.53) {
609 0 0       0 next PARAM_NAME if $action_id =~ /\A global_ /xms;
610             }
611              
612 6 50 33     43 next PARAM_NAME if $action_id =~ /(?: \A | _ ) direct \z/xms
      33        
613             or $action_id =~ /(?: \A | _ ) indirect \z/xms
614             or $action_id =~ /\A global__ /xms;
615              
616 6 50       119 return $param_name if $self->can("__${page_id}__$action_id");
617              
618 6 50       15 next PARAM_NAME if defined $global_action;
619              
620 6 50       12 if ($self->BCV < 0.53) {
621 0 0       0 if ( $self->can("global_$action_id") ) {
622 0         0 $global_action = "global_$param_name";
623             }
624              
625 0         0 next PARAM_NAME;
626             }
627              
628 6 100       41 if ( $self->can("global__$action_id") ) {
629 2         5 $global_action = "global__$param_name";
630             }
631              
632 6         14 next PARAM_NAME;
633             }
634              
635 2 50       9 return $global_action if defined $global_action;
636              
637 0 0       0 return 'submit' if $self->can("__${page_id}__submit");
638              
639 0 0       0 if ($self->BCV < 0.53) {
640 0 0       0 return 'global_submit' if $self->can('global_submit');
641             }
642              
643 0 0       0 return 'global__submit' if $self->can('global__submit');
644              
645 0         0 $self->warn('Requested parameters do not match with defined action');
646              
647 0         0 return;
648             }
649              
650 5     5 0 27 sub get_page { $_[0]->stash->{page} }
651              
652 2     2 1 8 sub page { shift->get_page(@_) }
653              
654             sub to_action_id {
655 6     6 0 13 my (undef, $action) = @_;
656              
657 6         9 my $action_id = $action;
658 6         10 $action_id =~ s/ \. .* \z//xms;
659              
660 6         13 return $action_id;
661             }
662              
663             sub set_action {
664 4     4 0 7 my ($self, $action) = @_;
665              
666 4         13 $self->stash->{action} = $action;
667              
668 4         9 return;
669             }
670              
671             sub initialize_response_headers {
672 3     3 0 6 my ($self) = @_;
673              
674 3         13 $self->set_response_headers( () );
675              
676 3         4 return;
677             }
678              
679             sub initialize_binmode {
680 3     3 0 6 my ($self) = @_;
681              
682 3 50       11 if ( $self->get_using_utf8 ) {
683 0         0 eval q{ binmode select, ':utf8' };
684             }
685             else {
686 23     23   368 no strict 'refs';
  23         72  
  23         65004  
687 3         57 binmode select;
688             }
689              
690 3         8 return;
691             }
692              
693             sub set_response_headers {
694 5     5 0 24 my ($self, @response_headers) = @_;
695              
696 5 50       13 if ($self->BCV < 0.53) {
697 0         0 $self->stash->{headers} = \@response_headers;
698              
699 0         0 return;
700             }
701              
702 5         22 $self->stash->{response_headers} = \@response_headers;
703              
704 5         14 return;
705             }
706              
707             sub controller {
708 0     0 0 0 my ($self, @relays) = @_;
709              
710 0 0 0     0 local $NEXT::SEEN if $NEXT::SEEN and $Waft::Correct_NEXT_DISTINCT;
711              
712 0 0       0 if ( my $coderef = $self->can('begin') ) {
713 0         0 @relays = $self->call_method($coderef, @relays);
714             }
715              
716 0         0 my $stash = $self->stash;
717 0         0 my $forward_count;
718             METHOD:
719 0         0 while ( not $stash->{responded} ) {
720 0 0       0 if ( my $coderef = $self->can('before') ) {
721 0         0 @relays = $self->call_method($coderef, @relays);
722              
723 0 0       0 last METHOD if $stash->{responded};
724             }
725              
726 0 0       0 if ( my $coderef = $self->find_action_method ) {
727 0         0 @relays = $self->call_method($coderef, @relays);
728              
729 0 0       0 last METHOD if $stash->{responded};
730              
731 0 0       0 if ($self->BCV < 0.53) {
732 0 0       0 if ( $self->to_action_id($self->get_action) eq 'template' ) {
733 0         0 @relays = $self->call_template('CURRENT', @relays);
734              
735 0 0       0 last METHOD if $stash->{responded};
736             }
737             }
738              
739 0         0 next METHOD;
740             }
741             else {
742 0         0 $self->set_action('template');
743             }
744              
745 0         0 @relays = $self->call_template('CURRENT', @relays);
746              
747 0 0       0 last METHOD if $stash->{responded};
748             }
749             continue {
750 0 0       0 $self->die('Methods called too many times in controller')
751             if ++$forward_count >= 5;
752             }
753              
754 0 0       0 if ( $self->can('end') ) {
755 0         0 my @return_values = $self->end(@relays);
756              
757 0 0       0 if ( @return_values ) {
758 0         0 @relays = @return_values;
759             }
760             }
761              
762 0 0       0 return wantarray ? @relays : $relays[0];
763             }
764              
765             sub call_method {
766 1     1 0 3 my ($self, $method_coderef, @args) = @_;
767              
768 1         4 my @return_values = $self->$method_coderef(@args);
769              
770 1 0       4 return wantarray ? @return_values : $return_values[0]
    50          
771             if $self->stash->{responded};
772              
773 1         8 require B;
774 1         18 my $method_name = B::svref_2object($method_coderef)->GV->NAME;
775              
776 1 50 33     12 if ( $method_name eq 'begin' || $method_name eq 'before'
      33        
777             and @return_values == 0
778             ) {
779 0         0 my $next = { page => 'CURRENT', action => undef };
780 0         0 @return_values = ($next, @args);
781             }
782              
783 1         3 my $next = shift @return_values;
784 1 50       9 my ($next_page, $next_action)
    50          
785             = ref $next eq 'ARRAY' ? @$next
786             : ref $next eq 'HASH' ? ($next->{page}, $next->{action})
787             : ($next, undef);
788              
789 1 50       4 if ( not defined $next_page ) {
790 1 50       6 $next_page = $method_name eq 'begin' ? 'CURRENT'
    50          
791             : $method_name eq 'before' ? 'CURRENT'
792             : 'TEMPLATE';
793             }
794              
795 1 50       4 if ( not defined $next_action ) {
796 1 50       4 $next_action = $next_page eq 'TEMPLATE' ? 'template'
797             : 'indirect';
798             }
799              
800 1 50 33     20 if ($next_page eq 'CURRENT' or $next_page eq 'TEMPLATE') {
801             # don't change page
802             }
803             else {
804 0         0 $self->set_page($next_page);
805             }
806              
807 1 50 0     6 if ( $next_page eq 'CURRENT'
      33        
808             and $method_name eq 'begin' || $method_name eq 'before'
809             ) {
810             # don't change action
811             }
812             else {
813 1         7 $self->set_action($next_action);
814             }
815              
816 1         4 return @return_values;
817             }
818              
819             sub find_action_method {
820 0     0 0 0 my ($self) = @_;
821              
822 0         0 my $page_id = $self->to_page_id($self->get_page);
823 0         0 my $action_id = $self->to_action_id($self->get_action);
824              
825 0 0       0 if ($self->BCV < 0.53) {
826 0 0       0 if ($action_id eq 'direct') {
    0          
    0          
827 0   0     0 return $self->can("__${page_id}__direct")
828             || $self->can("__${page_id}")
829             || $self->can('global_direct');
830             }
831             elsif ($action_id eq 'indirect') {
832 0   0     0 return $self->can("__${page_id}__indirect")
833             || $self->can("__${page_id}")
834             || $self->can('global_indirect');
835             }
836             elsif ( $action_id =~ /\A global_ /xms ) {
837 0         0 return $self->can($action_id);
838             }
839             }
840              
841 0 0       0 if ($action_id eq 'direct') {
    0          
    0          
842 0   0     0 return $self->can("__${page_id}__direct")
843             || $self->can("__${page_id}")
844             || $self->can('global__direct');
845             }
846             elsif ($action_id eq 'indirect') {
847 0   0     0 return $self->can("__${page_id}__indirect")
848             || $self->can("__${page_id}")
849             || $self->can('global__indirect');
850             }
851             elsif ( $action_id =~ /\A global__ /xms ) {
852 0         0 return $self->can($action_id);
853             }
854              
855 0         0 return $self->can("__${page_id}__$action_id");
856             }
857              
858 2     2 0 7 sub get_action { $_[0]->stash->{action} }
859              
860 2     2 1 16 sub action { shift->get_action(@_) }
861              
862             sub call_template {
863 1     1 1 8 my ($self, $page, @args) = @_;
864              
865 1 50       4 if ($self->BCV < 0.53) {
866 0         0 $page =~ s/ .+ :: //xms;
867             }
868              
869 1 50 33     11 if ($page eq 'CURRENT' or $page eq 'TEMPLATE') {
870 0         0 $page = $self->get_page;
871             }
872              
873 1         7 my ($template_file, $template_class) = $self->get_template_file($page);
874              
875 1 50       4 if ( not defined $template_file ) {
876 0         0 $self->warn(qq{Requested page "$page" is not found});
877              
878 0     0   0 my $goto_not_found_coderef = sub { shift; 'not_found.html', @_ };
  0         0  
  0         0  
879              
880 0         0 return $self->call_method($goto_not_found_coderef, @args);
881             }
882              
883 1         7 my $template_coderef
884             = $self->compile_template_file($template_file, $template_class);
885              
886 1         9 return $self->call_method($template_coderef, @args);
887             }
888              
889 0     0 0 0 sub include { shift->call_template(@_) }
890              
891             sub get_template_file {
892 1     1 0 3 my ($self, $page) = @_;
893              
894 1 50 33     8 if ($page eq 'CURRENT' or $page eq 'TEMPLATE') {
895 0         0 $page = $self->get_page;
896             }
897              
898 1 50       14 if ( File::Spec->file_name_is_absolute($page) ) {
899 0 0       0 return if not -f $page;
900              
901 0         0 my $template_file = $page;
902 0   0     0 my $template_class = $self->blessed || $self;
903              
904 0         0 return $template_file, $template_class;
905             }
906              
907 1         38 return $self->find_template_file($page);
908             }
909              
910             {
911             my %Cached_template_file;
912              
913             sub find_template_file {
914 25     25 0 7407 my ($self, $page) = @_;
915              
916 25   33     114 my $class = $self->blessed || $self;
917              
918 25 100 100     142 return @{ $Cached_template_file{$class, $page} }
  3         18  
919             if $Waft::Cache and exists $Cached_template_file{$class, $page};
920              
921 22         73 my ($template_file, $template_class)
922             = $self->recursive_find_template_file($page, $class);
923              
924 22 100       72 return if not defined $template_file;
925              
926 17         70 $Cached_template_file{$class, $page}
927             = [$template_file, $template_class];
928              
929 17         67 return $template_file, $template_class;
930             }
931             }
932              
933             sub recursive_find_template_file {
934 41     41 0 69 my ($self, $page, $class, $seen) = @_;
935              
936 41 50       138 return if $seen->{$class}++;
937              
938 41         55 my $class_path = $class;
939 41         144 $class_path =~ s{ :: }{/}gxms;
940              
941 41         80 my $module_file = "$class_path.pm";
942             my @lib_dirs
943 41 50       1056 = ! defined $INC{$module_file} ? @INC
    100          
944             : $INC{$module_file} =~ m{\A (.+) /\Q$module_file\E \z}xms ? ($1)
945             : @INC;
946              
947 41         136 my @finding_files;
948 41         95 push @finding_files, "$class_path.template/$page";
949 41 100       109 if ( $self->is_allowed_to_use_template_file_ext($page, $class) ) {
950 22         55 push @finding_files, "$class_path/$page";
951             }
952              
953 41         62 for my $lib_dir ( @lib_dirs ) {
954 59         76 for my $finding_file ( @finding_files ) {
955 88         160 my $template_file = "$lib_dir/$finding_file";
956              
957 88 100       1855 return $template_file, $class if -f $template_file;
958             }
959             }
960              
961 23     23   198 my @super_classes = do { no strict 'refs'; @{ "${class}::ISA" } };
  23         53  
  23         119346  
  24         34  
  24         24  
  24         129  
962 24         44 for my $super_class ( @super_classes ) {
963 19         85 my ($template_file, $template_class)
964             = $self->recursive_find_template_file($page, $super_class, $seen);
965              
966 19 100       84 return $template_file, $template_class if defined $template_file;
967             }
968              
969 15         50 return;
970             }
971              
972             sub is_allowed_to_use_template_file_ext {
973 41     41 0 67 my ($self, $page, $class) = @_;
974              
975 41 50       85 return if $self->BCV < 0.53;
976              
977             my @allow_template_file_exts
978 41         109 = $self->get_allow_template_file_exts($class);
979              
980             EXT:
981 41         84 for my $allow_template_file_ext ( @allow_template_file_exts ) {
982 67 50       127 if (length $allow_template_file_ext == 0) {
983 0 0       0 return 1 if $page !~ / \. /xms;
984              
985 0         0 next EXT;
986             }
987              
988 67 100       774 return 1 if $page =~ / \Q$allow_template_file_ext\E \z/xms;
989             }
990              
991 19         58 return;
992             }
993              
994             {
995             my %Cached_template_coderef;
996              
997             sub compile_template_file {
998 1     1 0 3 my ($self, $template_file, $template_class) = @_;
999              
1000 1         25 my @stat = stat $template_file;
1001 1 50       5 if ( not @stat ) {
1002 0         0 $self->warn(qq{Failed to stat template file "$template_file"});
1003              
1004             my $goto_internal_server_error_coderef
1005 0     0   0 = sub { shift; 'internal_server_error.html', @_ };
  0         0  
  0         0  
1006              
1007 0         0 return $goto_internal_server_error_coderef;
1008             }
1009 1         2 my $modified_time = $stat[9];
1010              
1011 1         4 my $template_name = "${template_class}::$template_file";
1012 1         5 my $template_id = "$template_name-$modified_time";
1013              
1014 1 50 33     7 return $Cached_template_coderef{$template_id}
1015             if $Waft::Cache and exists $Cached_template_coderef{$template_id};
1016              
1017 1         31 my $old_template_id_regexp = qr/\A \Q$template_name\E - \d{14} \z/xms;
1018             CACHED_TEMPLATE:
1019 1         5 for my $cached_template_id ( keys %Cached_template_coderef ) {
1020             next CACHED_TEMPLATE
1021 0 0       0 if $cached_template_id !~ $old_template_id_regexp;
1022 0         0 delete $Cached_template_coderef{$cached_template_id};
1023             }
1024              
1025 1         7 my $template_scalarref = $self->read_template_file($template_file);
1026 1 50       5 if ( not $template_scalarref ) {
1027 0         0 $self->warn(qq{Failed to read template file "$template_file"});
1028              
1029 0     0   0 my $goto_forbidden_coderef = sub { shift; 'forbidden.html', @_ };
  0         0  
  0         0  
1030              
1031 0         0 return $goto_forbidden_coderef;
1032             }
1033              
1034 1         9 my $template_coderef = $self->compile_template(
1035             $template_scalarref, $template_file, $template_class
1036             );
1037              
1038 1         3 $Cached_template_coderef{$template_id} = $template_coderef;
1039              
1040 1         7 return $template_coderef;
1041             }
1042             }
1043              
1044             sub read_template_file {
1045 1     1 0 3 my ($self, $template_file) = @_;
1046              
1047 1 50       7 sysopen my $file_handle = gensym, $template_file, O_RDONLY
1048             or return;
1049              
1050 1         72 binmode $file_handle;
1051              
1052 1         1 my ($untainted_template) = do { local $/; <$file_handle> =~ / (.*) /xms };
  1         5  
  1         38  
1053              
1054 1         13 close $file_handle;
1055              
1056 1         6 return \$untainted_template;
1057             }
1058              
1059             sub compile_template {
1060 2     2 0 13 my ($self, $template, $template_file, $template_class) = @_;
1061              
1062 2 100       9 if (ref $template eq 'SCALAR') {
1063 1         2 $template = $$template;
1064             }
1065              
1066 2         127 $template =~ s{ (?<=
) }
1067 0         0 { $self->insert_output_waft_tags_method($1) }egixms;
1068              
1069 2         14 $template =~ / ( \x0D\x0A | [\x0A\x0D] ) /xms;
1070 2   50     11 my $break = $1 || "\n";
1071              
1072 2         9 $template = "%>$template<%";
1073              
1074 2         30 $template =~ s{ (?<= %> ) (?! <% ) (.+?) (?= <% ) }
1075 28         62 { $self->convert_text_part($1, $break) }egxms;
1076              
1077 2         54 $template
1078             =~ s{<% (?! \s*[\x0A\x0D]
1079             =[A-Za-z]
1080             )
1081             \s* j(?:sstr)? \s* = (.*?)
1082             %>}{\$__self->output( \$__self->jsstr_filter($1) );}gxms;
1083              
1084 2         41 $template
1085             =~ s{<% (?! \s*[\x0A\x0D]
1086             =[A-Za-z]
1087             )
1088             \s* p(?:lain)? \s* = (.*?)
1089             %>}{\$__self->output($1);}gxms;
1090              
1091 2         39 $template
1092             =~ s{<% (?! \s*[\x0A\x0D]
1093             =[A-Za-z]
1094             )
1095             \s* t(?:ext)? \s* = (.*?)
1096             %>}{\$__self->output( \$__self->text_filter($1) );}gxms;
1097              
1098 2         50 $template
1099             =~ s{<% (?! \s*[\x0A\x0D]
1100             =[A-Za-z]
1101             )
1102             \s* (?: w(?:ord)? \s* )? = (.*?)
1103             %>}{\$__self->output( \$__self->word_filter($1) );}gxms;
1104              
1105 2         40 $template =~ s/ %> | <% //gxms;
1106              
1107 2 50       8 $template = 'return sub {'
1108             . ( $self->BCV < 1.0 ? 'local $Waft::Self = $_[0];' : q{} )
1109             . 'my $__self = $_[0];'
1110             . $template
1111             . '}';
1112              
1113 2 50       7 if ( defined $template_class ) {
1114 2         23 $template = "package $template_class;" . $template;
1115             }
1116              
1117 2 50       7 if ( defined $template_file ) {
1118 2         12 $template = qq{$break# line 1 "$template_file"$break} . $template;
1119             }
1120              
1121 2         15 my $coderef = $self->compile(\$template);
1122              
1123 2 50       191 $self->die($@) if $@;
1124              
1125 2         11 return $coderef;
1126             }
1127              
1128             sub insert_output_waft_tags_method {
1129 0     0 0 0 my ($self, $form_block) = @_;
1130              
1131 0 0       0 return $form_block if $form_block =~ m{ \b (?:
1132             output_waft_tags
1133             | (?: (?i) waft(?: \s+ | _ ) tag s? )
1134             | form_elements # deprecated
1135             ) \b }xms;
1136              
1137 0         0 $form_block =~ s{ (?= < (?: input | select | textarea | label ) \b ) }
1138             {<% \$__self->output_waft_tags('ALL_VALUES'); %>}ixms;
1139              
1140 0         0 return $form_block;
1141             }
1142              
1143             sub output_waft_tags {
1144 0     0 0 0 my ($self, @keys_arrayref_or_key_value_pairs) = @_;
1145              
1146 0         0 $self->output( $self->get_waft_tags(@keys_arrayref_or_key_value_pairs) );
1147              
1148 0         0 return;
1149             }
1150              
1151             sub get_waft_tags {
1152 0     0 0 0 my ($self, @keys_arrayref_or_key_value_pairs) = @_;
1153              
1154 0         0 my $joined_values = $self->join_values(@keys_arrayref_or_key_value_pairs);
1155 0         0 my $waft_tags = q{ 1156             . $self->html_escape($self->get_page)
1157             . q{" /> 1158             . $self->html_escape($joined_values)
1159             . q{" />};
1160              
1161 0         0 return $waft_tags;
1162             }
1163              
1164             sub join_values {
1165 13     13 0 41 my ($self, @keys_arrayref_or_key_value_pairs) = @_;
1166              
1167 13         21 my %joined_values;
1168              
1169 13         37 my $value_hashref = $self->value_hashref;
1170             KEYS_ARRAYREF_OR_KEY:
1171 13         57 while ( @keys_arrayref_or_key_value_pairs ) {
1172 5         11 my $keys_arrayref_or_key = shift @keys_arrayref_or_key_value_pairs;
1173              
1174 5 50 33     39 if ( defined $keys_arrayref_or_key
1175             and $keys_arrayref_or_key eq 'ALL_VALUES'
1176             ) {
1177 5         38 $keys_arrayref_or_key = [ keys %$value_hashref ];
1178             }
1179              
1180 5 50       23 if (ref $keys_arrayref_or_key eq 'ARRAY') {
1181             KEY:
1182 5         13 for my $key ( @$keys_arrayref_or_key ) {
1183 12 50       46 if ( not defined $key ) {
1184 0         0 $self->warn('Use of uninitialized value');
1185 0         0 $key = q{};
1186             }
1187              
1188 12 50       32 next KEY if not exists $value_hashref->{$key};
1189              
1190 12         44 my @values = $self->get_values($key);
1191              
1192             VALUE:
1193 12         25 for my $value ( @values ) {
1194 14 50       47 next VALUE if defined $value;
1195 0         0 $self->warn('Use of uninitialized value');
1196 0         0 $value = q{};
1197             }
1198              
1199 12         42 @values = $self->escape_space_percent_hyphen(@values);
1200              
1201 12         27 $joined_values{$key} = join q{}, map { "-$_" } @values;
  14         62  
1202             }
1203              
1204 5         24 next KEYS_ARRAYREF_OR_KEY;
1205             }
1206              
1207 0         0 my $key;
1208              
1209 0 0       0 if ( defined $keys_arrayref_or_key ) {
1210 0         0 $key = $keys_arrayref_or_key;
1211             }
1212             else {
1213 0         0 $self->warn('Use of uninitialized value');
1214 0         0 $key = q{};
1215             }
1216              
1217 0         0 my @values;
1218              
1219 0 0       0 if ( @keys_arrayref_or_key_value_pairs ) {
1220 0         0 my $value_or_values_arrayref
1221             = shift @keys_arrayref_or_key_value_pairs;
1222              
1223 0 0       0 if ( not defined $value_or_values_arrayref ) {
    0          
1224 0         0 $self->warn('Use of uninitialized value');
1225 0         0 @values = (q{});
1226             }
1227             elsif (ref $value_or_values_arrayref eq 'ARRAY') {
1228 0         0 @values = @$value_or_values_arrayref;
1229              
1230             VALUE:
1231 0         0 for my $value ( @values ) {
1232 0 0       0 next VALUE if defined $value;
1233 0         0 $self->warn('Use of uninitialized value');
1234 0         0 $value = q{};
1235             }
1236             }
1237             else {
1238 0         0 @values = ($value_or_values_arrayref);
1239             }
1240             }
1241             else {
1242 0         0 $self->warn('Odd number of elements in arguments');
1243 0         0 @values = (q{});
1244             }
1245              
1246 0         0 @values = $self->escape_space_percent_hyphen(@values);
1247              
1248 0         0 $joined_values{$key} = join q{}, map { "-$_" } @values;
  0         0  
1249              
1250 0         0 next KEYS_ARRAYREF_OR_KEY;
1251             }
1252              
1253 12         27 my $joined_values
1254 13         56 = join q{ }, map { $self->escape_space_percent_hyphen($_)
1255             . $joined_values{$_}
1256             } sort keys %joined_values;
1257              
1258 13         59 return $joined_values;
1259             }
1260              
1261             {
1262             my @EMPTY;
1263              
1264             sub get_values {
1265 13     13 1 27 my ($self, $key, @i) = @_;
1266              
1267 13 50       33 return @{ $self->value_hashref->{$key} || \@EMPTY }[@i] if @i;
  1 100       4  
1268              
1269 12 50       14 return @{ $self->value_hashref->{$key} || \@EMPTY };
  12         26  
1270             }
1271             }
1272              
1273             sub escape_space_percent_hyphen {
1274 24     24 0 48 my (undef, @values) = @_;
1275              
1276 24         40 for my $value (@values) {
1277 26         79 $value =~ s/ ( [ %-] ) / '%' . unpack('H2', $1) /egxms;
  24         112  
1278             }
1279              
1280 24 100       139 return wantarray ? @values : $values[0];
1281             }
1282              
1283             sub convert_text_part {
1284 28     28 0 58 my (undef, $text_part, $break) = @_;
1285              
1286 28 100       96 if ($text_part =~ / ([^\x0A\x0D]*) ( [\x0A\x0D] .* ) /xms) {
1287 21         35 my ($first_line, $after_first_break) = ($1, $2);
1288              
1289 21 100       39 if (length $first_line > 0) {
1290 5         14 $first_line =~ s/ ( ['\\] ) /\\$1/gxms;
1291 5         11 $first_line = q{$__self->output('} . $first_line . q{');};
1292             }
1293              
1294 21         45 $after_first_break =~ s/ ( ["\$\@\\] ) /\\$1/gxms;
1295              
1296 21         71 my $breaks = $break x (
1297             $after_first_break =~ s/ \x0D\x0A /\\x0D\\x0A/gxms
1298             + $after_first_break =~ s/ \x0A /\\x0A/gxms
1299             + $after_first_break =~ s/ \x0D /\\x0D/gxms
1300             - 1
1301             );
1302              
1303 21         161 return $first_line . $break
1304             . qq{\$__self->output("$after_first_break");$breaks};
1305             }
1306              
1307 7         52 $text_part =~ s/ ( ['\\] ) /\\$1/gxms;
1308              
1309 7         53 return q{$__self->output('} . $text_part . q{');};
1310             }
1311              
1312             {
1313             package Waft::compile;
1314              
1315 23     23   231 no strict;
  23         59  
  23         1781  
1316 23 50   23   58 BEGIN { 'warnings'->unimport if eval { require warnings } }
  23         60558  
1317              
1318 2     2 0 4 sub Waft::compile { eval ${ $_[1] } }
  2         1094  
1319             }
1320              
1321             {
1322             my $OUTPUT_CONTENT_CODEREF;
1323              
1324             sub output {
1325 8   66 8 1 63 ( $_[0]->stash->{output} ||= do {
1326 2         3 my ($self) = @_;
1327              
1328 2         9 $self->output_response_headers;
1329 2         5 $self->stash->{responded} = 1;
1330              
1331 2         10 $OUTPUT_CONTENT_CODEREF;
1332             } )->(@_);
1333             }
1334              
1335             $OUTPUT_CONTENT_CODEREF = sub { shift; print @_ if @_; return };
1336             }
1337              
1338             sub output_response_headers {
1339 2     2 0 4 my ($self) = @_;
1340              
1341 2         2 my ($http_status, $content_type);
1342              
1343             RESPONSE_HEADER:
1344 2         11 for my $response_header ( $self->get_response_headers ) {
1345 2         104 print "$response_header\x0D\x0A";
1346              
1347 2 100       14 if ( $response_header =~ /\A Status: \s* (.*) /ixms ) {
1348 1         3 $http_status = $1;
1349             }
1350              
1351 2 100       12 if ( $response_header =~ /\A Content-Type: \s* (.*) /ixms ) {
1352 1         10 $content_type = $1;
1353             }
1354             }
1355              
1356 2 50       7 if ($self->BCV < 0.53) {
1357 0 0       0 if ( not defined $content_type ) {
1358 0         0 $content_type = $self->stash->{content_type};
1359 0         0 print "Content-Type: $content_type\x0D\x0A";
1360             }
1361              
1362 0         0 print "\x0D\x0A";
1363              
1364 0         0 return;
1365             }
1366              
1367 2 100       14 if ( not defined $content_type ) {
1368 1         9 $content_type = $self->get_default_content_type;
1369 1         15 print "Content-Type: $content_type\x0D\x0A";
1370             }
1371              
1372 2         19 print "\x0D\x0A";
1373              
1374 2         4 @{ $self->stash }{ qw( http_status content_type ) }
  2         4  
1375             = ($http_status, $content_type);
1376              
1377 2         4 return;
1378             }
1379              
1380             sub get_response_headers {
1381 4     4 0 9 my ($self) = @_;
1382              
1383 4 50       14 return @{ $self->stash->{headers} } if $self->BCV < 0.53;
  0         0  
1384              
1385 4         7 return @{ $self->stash->{response_headers} }
  4         9  
1386             }
1387              
1388 1     1 0 11 sub responded { $_[0]->stash->{responded} }
1389              
1390             {
1391             my $BUFFER_CONTENT_CODEREF;
1392              
1393             sub get_content {
1394 4     4 1 47 my ($self, $coderef, @args) = @_;
1395              
1396 4         25 my $stash = $self->stash;
1397              
1398 4         7 push @{ $stash->{contents} }, undef;
  4         10  
1399              
1400 4         19 local $stash->{output} = $BUFFER_CONTENT_CODEREF
1401 4 100       7 if @{ $stash->{contents} } == 1;
1402 4         15 my @return_values = $self->$coderef(@args);
1403              
1404 4 100       17 return pop @{ $stash->{contents} }, @return_values if wantarray;
  1         5  
1405 3         5 return pop @{ $stash->{contents} };
  3         17  
1406             }
1407              
1408             $BUFFER_CONTENT_CODEREF = sub {
1409             shift->stash->{contents}[-1] .= join q{}, @_ if @_ > 1; return;
1410             };
1411             }
1412              
1413 10     10 0 91 sub jsstr_filter { shift->jsstr_escape(@_) }
1414              
1415             sub jsstr_escape {
1416 10     10 1 15 my ($self, @values) = @_;
1417              
1418             VALUE:
1419 10         15 for my $value (@values) {
1420 10 50       24 if ( not defined $value ) {
1421 0         0 $self->warn('Use of uninitialized value');
1422              
1423 0         0 next VALUE;
1424             }
1425              
1426 10         49 $value =~ s{ (["'/\\]) }{\\$1}gxms;
1427 10         21 $value =~ s/ \x0A /\\n/gxms;
1428 10         18 $value =~ s/ \x0D /\\r/gxms;
1429 10         15 $value =~ s/ < /\\x3C/gxms;
1430 10         25 $value =~ s/ > /\\x3E/gxms;
1431             }
1432              
1433 10 100       58 return wantarray ? @values : $values[0];
1434             }
1435              
1436             sub text_filter {
1437 10     10 0 34 my ($self, @values) = @_;
1438              
1439             VALUE:
1440 10         16 for my $value ( @values ) {
1441 10 50       27 if ( not defined $value ) {
1442 0         0 $self->warn('Use of uninitialized value');
1443              
1444 0         0 next VALUE;
1445             }
1446              
1447 10         27 $value = $self->expand_tabs($value);
1448 10         29 $value = $self->html_escape($value);
1449 10         60 $value =~ s{ ( \x0D\x0A | [\x0A\x0D] ) }{
$1}gxms;
1450 10         22 $value =~ s{\A \x20 }{ }gxms;
1451 10         60 $value =~ s{ (\s) \x20 }{$1 }gxms;
1452             }
1453              
1454 10 100       70 return wantarray ? @values : $values[0];
1455             }
1456              
1457             sub expand_tabs {
1458 12     12 0 35 my ($self, @values) = @_;
1459              
1460             VALUE:
1461 12         19 for my $value (@values) {
1462 12 50       42 if ( not defined $value ) {
1463 0         0 $self->warn('Use of uninitialized value');
1464              
1465 0         0 next VALUE;
1466             }
1467              
1468 12         54 $value =~ s{( [^\x0A\x0D]+ )}{
1469 51         74 my $line = $1;
1470              
1471 51         136 while ( $line =~ / \t /gxms ) {
1472 106         117 my $offset = pos($line) - 1;
1473 106         295 substr( $line, $offset, 1 ) = q{ } x ( 8 - $offset % 8 );
1474             }
1475              
1476 51         195 $line;
1477             }egxms;
1478             }
1479              
1480 12 50       51 return wantarray ? @values : $values[0];
1481             }
1482              
1483             sub html_escape {
1484 45     45 1 81 my ($self, @values) = @_;
1485              
1486             VALUE:
1487 45         76 for my $value (@values) {
1488 45 50       98 if ( not defined $value ) {
1489 0         0 $self->warn('Use of uninitialized value');
1490              
1491 0         0 next VALUE;
1492             }
1493              
1494 45         92 $value =~ s/ & /&/gxms;
1495 45         70 $value =~ s/ " /"/gxms;
1496 45         142 $value =~ s/ ' /'/gxms;
1497 45         75 $value =~ s/ < /</gxms;
1498 45         113 $value =~ s/ > />/gxms;
1499             }
1500              
1501 45 100       388 return wantarray ? @values : $values[0];
1502             }
1503              
1504 18     18 0 52 sub word_filter { shift->html_escape(@_) }
1505              
1506             {
1507             my (%Start, %Progress, $FIND_NEXT_CODEREF);
1508              
1509             sub next {
1510 84     84 1 783 my ($self) = @_;
1511              
1512 84         87 my ($back, $subroutine);
1513 84         748 1 while ( ( $subroutine = ( caller ++$back )[3] ) eq '(eval)' );
1514 84         467 my ($caller, $method) = $subroutine =~ / (.+) :: (.+) /xms;
1515              
1516 84   66     493 my $ident = $self->refaddr || $self;
1517              
1518 84 100 100     927 local $Start{ $ident, $method } = $caller
1519             if not $Start{ $ident, $method }
1520             or ( caller $back + 1 )[3] ne ( caller 0 )[3];
1521 84         500 local $Progress{ $ident, $method, $Start{ $ident, $method } }
1522             = $Progress{ $ident, $method, $Start{ $ident, $method } };
1523              
1524 84         372 my $next_coderef = $self->$FIND_NEXT_CODEREF(
1525             $method
1526             , $Start{ $ident, $method }
1527             , $Progress{ $ident, $method, $Start{ $ident, $method } }++
1528             );
1529              
1530 84 50       285 return if not $next_coderef;
1531              
1532 84         492 return $next_coderef->(@_);
1533             }
1534              
1535             my %Cached_next_coderefs;
1536              
1537             $FIND_NEXT_CODEREF = sub {
1538             my ($self, $method, $start, $progress) = @_;
1539              
1540             my $class = $self->blessed || $self;
1541              
1542             return $Cached_next_coderefs{$class, $method, $start}->[$progress]
1543             if $Waft::Cache
1544             and exists $Cached_next_coderefs{$class, $method, $start};
1545              
1546             my @next_classes;
1547              
1548             my @classes = ($class);
1549             while ( my $class = shift @classes ) {
1550             push @next_classes, $class;
1551              
1552 23     23   218 no strict 'refs';
  23         55  
  23         6076  
1553             unshift @classes, @{ "${class}::ISA" };
1554             }
1555              
1556             while ( $start ne shift @next_classes ) {
1557             return if @next_classes == 0;
1558             }
1559              
1560             my @next_coderefs = do {
1561 23     23   285 no strict 'refs';
  23         52  
  23         190586  
1562             grep { $_ } map { *{ "${_}::$method" }{CODE} } @next_classes;
1563             };
1564              
1565             $Cached_next_coderefs{$class, $method, $start} = \@next_coderefs;
1566              
1567             return $next_coderefs[$progress];
1568             };
1569             }
1570              
1571             sub get_page_id {
1572 0     0 0 0 my ($self, $page) = @_;
1573              
1574 0 0       0 if ( not defined $page ) {
1575 0         0 $page = $self->get_page;
1576             }
1577              
1578 0         0 my $page_id = $self->to_page_id($page);
1579              
1580 0         0 return $page_id;
1581             }
1582              
1583 0     0 0 0 sub page_id { shift->get_page_id(@_) }
1584              
1585             sub set_value {
1586 0     0 1 0 my ($self, $key, $value) = @_;
1587              
1588 0         0 $self->set_values($key, $value);
1589              
1590 0         0 return;
1591             }
1592              
1593             sub get_value {
1594 1     1 1 4 my ($self, $key, @i) = @_;
1595              
1596 1         3 return( ( $self->get_values($key, @i) )[0] );
1597             }
1598              
1599             sub http_status {
1600 4 100   4 1 10854 return $_[0]->stash->{http_status} if @_ == 1;
1601 1         2 my ($self, $http_status) = @_;
1602              
1603 1         5 $self->set_response_header("Status: $http_status");
1604              
1605 1         2 return;
1606             }
1607              
1608             sub content_type {
1609 3 100   3 1 23 return $_[0]->stash->{content_type} if @_ == 1;
1610 1         3 my ($self, $content_type) = @_;
1611              
1612 1         10 $self->set_response_header("Content-Type: $content_type");
1613              
1614 1         2 return;
1615             }
1616              
1617             sub set_response_header {
1618 2     2 0 5 my ($self, $response_header) = @_;
1619              
1620 2 50       5 if ( $self->stash->{responded} ) {
1621 0         0 $self->warn('Too late to set response header');
1622              
1623 0         0 return;
1624             }
1625              
1626 2 50       13 if ( $response_header =~ /\A ([^:]+) /xms ) {
1627 2         8 my $field = $1;
1628 2         10 $self->unset_response_header($field);
1629             }
1630              
1631 2         7 $self->add_response_header($response_header);
1632              
1633 2         4 return;
1634             }
1635              
1636             sub unset_response_header {
1637 2     2 0 5 my ($self, $response_header_field) = @_;
1638              
1639 2         4 my $stash = $self->stash;
1640              
1641 2 50       7 if ( $stash->{responded} ) {
1642 0         0 $self->warn('Too late to unset response header');
1643              
1644 0         0 return;
1645             }
1646              
1647 2         6 @{ $stash->{response_headers} }
  4         66  
1648 2         6 = grep { not /\A \Q$response_header_field\E: /ixms }
1649 2         3 @{ $stash->{response_headers} };
1650              
1651 2         5 return;
1652             }
1653              
1654             sub add_response_header {
1655 6     6 0 8 my ($self, $response_header) = @_;
1656              
1657 6 50       13 if ($_[0]->BCV < 0.53) {
1658 0         0 ( undef, my @response_header_blocks ) = @_;
1659              
1660 0         0 my $stash = $self->stash;
1661 0         0 for my $response_header_block ( @response_header_blocks ) {
1662             my @response_header_lines
1663 0         0 = grep { length > 0 }
  0         0  
1664             split /[\x0A\x0D]+/, $response_header_block;
1665 0         0 push @{ $stash->{headers} }, @response_header_lines;
  0         0  
1666             }
1667              
1668 0         0 return;
1669             }
1670              
1671 6         17 my $stash = $self->stash;
1672              
1673 6 50       14 if ( $stash->{responded} ) {
1674 0         0 $self->warn('Too late to add response header');
1675              
1676 0         0 return;
1677             }
1678              
1679 6         13 $response_header =~ s/ [\x0A\x0D]+ //gxms;
1680 6         9 push @{ $stash->{response_headers} }, $response_header;
  6         13  
1681              
1682 6         13 return;
1683             }
1684              
1685 4     4 1 30 sub header { shift->add_response_header(@_) }
1686              
1687 0     0 0 0 sub add_header { shift->header(@_) }
1688              
1689             sub make_url {
1690 9     9 0 17 my ($self, $page, @keys_arrayref_or_key_value_pairs) = @_;
1691              
1692 9         32 my $query_string
1693             = $self->make_query_string($page, @keys_arrayref_or_key_value_pairs);
1694              
1695 9 100       49 return $self->get_base_url if length $query_string == 0;
1696              
1697 1         2 return $self->get_base_url . '?' . $query_string;
1698             }
1699              
1700 2     2 1 25 sub url { shift->make_url(@_) }
1701              
1702             sub make_absolute_url {
1703 7     7 0 14 my ($self, @args) = @_;
1704              
1705 7         20 my $protocol = $self->cgi->protocol;
1706              
1707 7         1490 my $base_url = "$protocol://";
1708              
1709 7 100       19 if ( defined $ENV{HTTP_HOST} ) {
1710 1         4 $base_url .= $ENV{HTTP_HOST};
1711             }
1712             else {
1713 6         10 $base_url .= $ENV{SERVER_NAME};
1714              
1715 6 100 100     49 if ( $protocol eq 'http' and $ENV{SERVER_PORT} != 80
      100        
      66        
1716             or $protocol eq 'https' and $ENV{SERVER_PORT} != 443
1717             ) {
1718 2         6 $base_url .= ":$ENV{SERVER_PORT}";
1719             }
1720             }
1721              
1722 7 100 66     39 if ( defined $ENV{REQUEST_URI}
1723             and $ENV{REQUEST_URI} =~ /\A ([^?]+) /xms
1724             ) {
1725 5         14 $base_url .= $1;
1726             }
1727             else {
1728 2         6 $base_url .= $ENV{SCRIPT_NAME};
1729             }
1730              
1731 7 50       16 local $self->stash->{url} = $base_url if $self->BCV < 0.53;
1732 7         16 local $self->stash->{base_url} = $base_url;
1733              
1734 7         23 return $self->make_url(@args);
1735             }
1736              
1737 7     7 1 83 sub absolute_url { shift->make_absolute_url(@_) }
1738              
1739             sub make_query_string {
1740 9     9 0 17 my ($self, $page, @keys_arrayref_or_key_value_pairs) = @_;
1741              
1742 9 50       23 if (ref $page eq 'ARRAY') {
1743 0         0 $page = $page->[0];
1744             }
1745              
1746 9 50       24 $page = ! defined $page ? 'default.html'
    100          
1747             : $page eq 'CURRENT' ? $self->get_page
1748             : $page;
1749              
1750 9         13 my @query_string;
1751              
1752 9 100       23 if ($page ne 'default.html') {
1753 1         7 push @query_string,
1754             join( '=', ( $self->url_encode( 'p' => $page ) ) );
1755             }
1756              
1757 9         35 my $joined_values = $self->join_values(@keys_arrayref_or_key_value_pairs);
1758 9 100       22 if ( $joined_values ) {
1759 1         3 push @query_string,
1760             join( '=', ( $self->url_encode('v' => $joined_values) ) );
1761             }
1762              
1763 9         16 my $query_string = join '&', @query_string;
1764              
1765 9         28 return $query_string;
1766             }
1767              
1768             sub url_encode {
1769 2     2 1 5 my ($self, @values) = @_;
1770              
1771 2         6 my $using_utf8 = $self->get_using_utf8;
1772              
1773             VALUE:
1774 2         4 for my $value ( @values ) {
1775 4 50       10 if ( not defined $value ) {
1776 0         0 $self->warn('Use of uninitialized value');
1777              
1778 0         0 next VALUE;
1779             }
1780              
1781 4 50       7 if ( $using_utf8 ) {
1782 0         0 utf8::encode($value);
1783             }
1784              
1785 4         5 $value =~ s/ ( [^ .\w-] ) / '%' . unpack('H2', $1) /egxms;
  0         0  
1786 4         11 $value =~ tr/ /+/;
1787             }
1788              
1789 2 50       11 return wantarray ? @values : $values[0];
1790             }
1791              
1792             sub get_base_url {
1793 9     9 0 14 my ($self) = @_;
1794              
1795 9 50 33     27 return $Waft::Base_url if defined $Waft::Base_url and $self->BCV < 1.0;
1796              
1797 9 50       23 return $self->stash->{url} if $self->BCV < 0.53;
1798              
1799 9         18 return $self->stash->{base_url};
1800             }
1801              
1802             sub __forbidden__indirect {
1803 0     0   0 my ($self, @args) = @_;
1804              
1805 0         0 $self->http_status('403 Forbidden');
1806 0         0 $self->content_type('text/html; charset=ISO8859-1');
1807              
1808 0         0 my $escaped_request_uri = $self->html_escape($ENV{REQUEST_URI});
1809              
1810 0         0 $self->output(qq{\n});
1811 0         0 $self->output(qq{\n});
1812 0         0 $self->output(qq{403 Forbidden\n});
1813 0         0 $self->output(qq{\n});
1814 0         0 $self->output(qq{

Forbidden

\n});
1815 0         0 $self->output( q{

You don't have permission to access});

1816 0         0 $self->output(qq{ $escaped_request_uri\non this server.

\n});
1817 0         0 $self->output(qq{\n});
1818              
1819 0         0 return @args;
1820             }
1821              
1822             sub __not_found__indirect {
1823 0     0   0 my ($self, @args) = @_;
1824              
1825 0         0 $self->http_status('404 Not Found');
1826 0         0 $self->content_type('text/html; charset=ISO8859-1');
1827              
1828 0         0 my $escaped_request_uri = $self->html_escape($ENV{REQUEST_URI});
1829              
1830 0         0 $self->output(qq{\n});
1831 0         0 $self->output(qq{\n});
1832 0         0 $self->output(qq{404 Not Found\n});
1833 0         0 $self->output(qq{\n});
1834 0         0 $self->output(qq{

Not Found

\n});
1835 0         0 $self->output(qq{

The requested URL $escaped_request_uri});

1836 0         0 $self->output(qq{ was not found on this server.

\n});
1837 0         0 $self->output(qq{\n});
1838              
1839 0         0 return @args;
1840             }
1841              
1842             sub __internal_server_error__indirect {
1843 0     0   0 my ($self, @args) = @_;
1844              
1845 0         0 $self->http_status('500 Internal Server Error');
1846 0         0 $self->content_type('text/html; charset=ISO8859-1');
1847              
1848 0         0 my $escaped_server_admin = $self->html_escape($ENV{SERVER_ADMIN});
1849              
1850 0         0 $self->output(qq{\n});
1851 0         0 $self->output(qq{\n});
1852 0         0 $self->output(qq{500 Internal Server Error\n});
1853 0         0 $self->output(qq{\n});
1854 0         0 $self->output(qq{

Internal Server Error

\n});
1855 0         0 $self->output(qq{

The server encountered an internal error or\n});

1856 0         0 $self->output(qq{misconfiguration and was unable to complete\n});
1857 0         0 $self->output(qq{your request.

\n});
1858 0         0 $self->output(qq{

Please contact the server administrator,\n});

1859 0         0 $self->output(qq{ $escaped_server_admin});
1860 0         0 $self->output(qq{ and inform them of the time the error occurred,\n});
1861 0         0 $self->output(qq{and anything you might have done that may have\n});
1862 0         0 $self->output(qq{caused the error.

\n});
1863 0         0 $self->output( q{

More information about this error may be });

1864 0         0 $self->output(qq{available\n});
1865 0         0 $self->output(qq{in the server error log.

\n});
1866 0         0 $self->output(qq{\n});
1867              
1868 0         0 return @args;
1869             }
1870              
1871             {
1872             my $Defined_subs_for_under_0_99x;
1873              
1874             sub define_subs_for_under_0_99x {
1875              
1876 2 50   2 0 19 return if $Defined_subs_for_under_0_99x;
1877              
1878 2     0   22 *croak = *croak = sub { shift->die(@_) };
  0         0  
1879 2     0   10 *carp = *carp = sub { shift->warn(@_) };
  0         0  
1880              
1881             *init_base_url = *init_base_url
1882 2     1   10 = sub { shift->initialize_base_url(@_) };
  1         20  
1883             *init_page = *init_page
1884 2     1   11 = sub { shift->initialize_page(@_) };
  1         12  
1885             *init_values = *init_values
1886 2     1   8 = sub { shift->initialize_values(@_) };
  1         12  
1887             *init_action = *init_action
1888 2     1   10 = sub { shift->initialize_action(@_) };
  1         13  
1889             *init_response_headers = *init_response_headers
1890 2     1   9 = sub { shift->initialize_response_headers(@_) };
  1         11  
1891             *init_binmode = *init_binmode
1892 2     1   11 = sub { shift->initialize_binmode(@_) };
  1         13  
1893              
1894 2     0   9 *is_blessed = *is_blessed = sub { shift->blessed(@_) };
  0         0  
1895              
1896 2     0   10 *ident = *ident = sub { shift->refaddr(@_) };
  0         0  
1897              
1898             *keys_arrayref = *keys_arrayref
1899 2     0   11 = sub { [ keys %{ $_[0]->value_hashref } ] };
  0         0  
  0         0  
1900              
1901             *exists_key = *exists_key
1902 2     0   8 = sub { exists $_[0]->value_hashref->{ $_[1] } };
  0         0  
1903              
1904 2     1   17 *expand = *expand = sub { Waft->expand_tabs(@_) };
  1         3  
1905              
1906 2         4 $Defined_subs_for_under_0_99x = 1;
1907              
1908 2         5 return;
1909             }
1910              
1911             my $Defined_subs_for_under_0_52x;
1912              
1913             sub define_subs_for_under_0_52x {
1914              
1915 0 0   0 0 0 return if $Defined_subs_for_under_0_52x;
1916              
1917             *array = *array = sub {
1918 0     0   0 my ($self, $key, @values) = @_;
1919              
1920 0 0       0 if ( @values ) {
1921 0         0 my @old_values = $self->get_values($key);
1922              
1923 0         0 $self->set_values($key, @values);
1924              
1925 0         0 return @old_values;
1926             }
1927              
1928 0         0 return $self->get_values($key);
1929 0         0 };
1930              
1931             *arrayref = *arrayref = sub {
1932 0     0   0 my ($self, $key, $arrayref) = @_;
1933              
1934 0 0       0 return $self->value_hashref->{$key} = $arrayref
1935             if ref $arrayref eq 'ARRAY';
1936              
1937 0   0     0 return $self->value_hashref->{$key} ||= $arrayref;
1938 0         0 };
1939              
1940 0         0 eval q{ sub begin { return } };
1941 0         0 eval q{ sub before { return } };
1942              
1943 0     0   0 *end = *end = sub { return };
  0         0  
1944              
1945             *form_elements = *form_elements = sub {
1946 0     0   0 my ($self, @args) = @_;
1947              
1948 0 0 0     0 if (@args == 1
      0        
      0        
1949             and defined $args[0]
1950             and $args[0] eq 'ALL' || $args[0] eq 'ALLVALUES'
1951             ) {
1952 0         0 $args[0] = 'ALL_VALUES';
1953             }
1954              
1955 0         0 $self->output_waft_tags(@args);
1956              
1957 0         0 return;
1958 0         0 };
1959              
1960 0         0 *query = *query = \&cgi;
1961              
1962 0         0 *waft_tags = *waft_tags = \&get_waft_tags;
1963              
1964 0         0 *_join_values = *_join_values = \&join_values;
1965              
1966             *_load_query_param = *_load_query_param = sub {
1967 0     0   0 my ($self) = @_;
1968              
1969 0         0 $self->init_page;
1970 0         0 $self->init_action;
1971 0         0 $self->init_values;
1972              
1973 0         0 return;
1974 0         0 };
1975              
1976             *__DEFAULT = *__DEFAULT = sub {
1977 0     0   0 my ($self, @args) = @_;
1978              
1979 0         0 return { page => 'default.html', action => $self->action }, @args;
1980 0         0 };
1981              
1982 0         0 $Defined_subs_for_under_0_52x = 1;
1983              
1984 0         0 return;
1985             }
1986             }
1987              
1988             package Waft::Object;
1989              
1990             sub TIEHASH {
1991              
1992 18     18   73 bless {};
1993             }
1994              
1995             sub STORE {
1996 3 50   3   25 if (ref $_[2] eq 'ARRAY') {
1997 0 0       0 @{ $_[0]{ defined $_[1] ? $_[1] : warn_and_null() } } = @{$_[2]};
  0         0  
  0         0  
1998             }
1999             else {
2000 3 50       6 @{ $_[0]{ defined $_[1] ? $_[1] : warn_and_null() } } = ($_[2]);
  3         32  
2001             }
2002             }
2003              
2004             sub warn_and_null {
2005 0     0   0 require Carp;
2006 0         0 Carp::carp('Use of uninitialized value');
2007 0         0 q{};
2008             }
2009              
2010             sub FETCH {
2011 6 50   6   35 my $arrayref = $_[0]{ defined $_[1] ? $_[1] : warn_and_null() }
    50          
2012             or return;
2013              
2014 6         44 $arrayref->[0];
2015             }
2016              
2017 0     0   0 sub FIRSTKEY { keys %{$_[0]}; each %{$_[0]} }
  0         0  
  0         0  
  0         0  
2018              
2019 0     0   0 sub NEXTKEY { each %{$_[0]} }
  0         0  
2020              
2021 0 0   0   0 sub EXISTS { exists $_[0]{ defined $_[1] ? $_[1] : warn_and_null() } }
2022              
2023 0 0   0   0 sub DELETE { delete $_[0]{ defined $_[1] ? $_[1] : warn_and_null() } }
2024              
2025 0     0   0 sub CLEAR { %{$_[0]} = () }
  0         0  
2026              
2027             1;
2028             __END__