File Coverage

lib/App/Request/CGI.pm
Criterion Covered Total %
statement 38 277 13.7
branch 12 192 6.2
condition 4 99 4.0
subroutine 5 11 45.4
pod 5 6 83.3
total 64 585 10.9


line stmt bran cond sub pod time code
1              
2             #############################################################################
3             ## $Id: CGI.pm 13908 2010-04-19 18:23:51Z spadkins $
4             #############################################################################
5              
6             package App::Request::CGI;
7             $VERSION = (q$Revision: 13908 $ =~ /(\d[\d\.]*)/)[0]; # VERSION numbers generated by svn
8              
9 1     1   14 use App;
  1         2  
  1         25  
10 1     1   708 use App::Request;
  1         2  
  1         42  
11             @ISA = ( "App::Request" );
12 1     1   7163 use CGI;
  1         20312  
  1         7  
13              
14 1     1   57 use strict;
  1         2  
  1         3458  
15              
16             =head1 NAME
17              
18             App::Request::CGI - the request
19              
20             =head1 SYNOPSIS
21              
22             # ... official way to get a Request object ...
23             use App;
24             $context = App->context();
25             $request = $context->request(); # get the request
26              
27             # ... alternative way (used internally) ...
28             use App::Request::CGI;
29             $request = App::Request::CGI->new();
30              
31             =cut
32              
33             #############################################################################
34             # CONSTANTS
35             #############################################################################
36              
37             =head1 DESCRIPTION
38              
39             A Request class implemented using the CGI class.
40              
41             =cut
42              
43             #############################################################################
44             # PROTECTED METHODS
45             #############################################################################
46              
47             =head1 Protected Methods:
48              
49             The following methods are intended to be called by subclasses of the
50             current class (or environmental, "main" code).
51              
52             =cut
53              
54             #############################################################################
55             # _init()
56             #############################################################################
57              
58             =head2 _init()
59              
60             The _init() method is called from within the standard Request constructor.
61             The _init() method in this class does nothing.
62             It allows subclasses of the Request to customize the behavior of the
63             constructor by overriding the _init() method.
64              
65             * Signature: $request->_init()
66             * Param: void
67             * Return: void
68             * Throws: App::Exception
69             * Since: 0.01
70              
71             Sample Usage:
72              
73             $request->_init();
74              
75             =cut
76              
77             sub _init {
78 1 50   1   5 &App::sub_entry if ($App::trace);
79 1         3 my ($self, $options) = @_;
80 1         2 my ($cgi, $var, $value, $app, $file);
81 1 50       4 $options = {} if (!defined $options);
82              
83 1         3 $app = $options->{app};
84 1 50       3 if (!defined $app) {
85             # untaint the $app
86 1         4 $0 =~ /(.*)/;
87 1         4 $app = $1;
88 1         2 $app =~ s!\\!/!g;
89 1         6 $app =~ s!\.[a-z]+$!!i;
90 1         3 $app =~ s!.*/!!;
91             }
92              
93 1   50     10 my $debug_request = $options->{debug_request} || "";
94 1   33     10 my $replay = ($debug_request eq "replay" || $options->{replay});
95 1   33     5 my $record = ($debug_request eq "record" && !$replay);
96              
97             #################################################################
98             # read environment variables
99             #################################################################
100              
101 1 50       5 if ($replay) {
102 0   0     0 $file = $options->{replay_env} || "$app.env";
103 0 0       0 if (open(App::FILE, "< $file")) {
104 0         0 foreach $var (keys %ENV) {
105 0         0 delete $ENV{$var}; # unset all environment variables
106             }
107 0         0 while () {
108 0         0 chop;
109 0         0 /^([^=]+)=(.*)/; # parse variable, value (and untaint)
110 0         0 $var = $1; # get variable name
111 0         0 $value = $2; # get variable value
112 0         0 $ENV{$var} = $value; # restore environment variable
113             }
114 0         0 close(App::FILE);
115             }
116             }
117              
118 1 50       4 if ($record) {
119 0         0 $file = "$app.env";
120 0 0       0 if (open(App::FILE, "> $file")) {
121 0         0 foreach $var (keys %ENV) {
122 0         0 print App::FILE "$var=$ENV{$var}\n"; # save environment variables
123             }
124 0         0 close(App::FILE);
125             }
126             }
127              
128             #################################################################
129             # READ HTTP PARAMETERS (CGI VARIABLES)
130             #################################################################
131              
132 1 50       3 if ($replay) {
133             # when the "debug_request" is in "replay", the saved CGI environment from
134             # a previous query (when "debug_request" was "record") is used
135 0   0     0 $file = $options->{replay_vars} || "$app.vars";
136 0 0       0 open(App::FILE, "< $file") || die "Unable to open $file: $!";
137 0         0 $cgi = new CGI(*App::FILE); # Get vars from debug file
138 0         0 close(App::FILE);
139             }
140             else { # ... the normal path
141 1 50 33     26 if (defined $options && defined $options->{cgi}) {
142             # this allows for migration from old scripts where they already
143             # read in the CGI object and they pass it in to App-Context as an arg
144 0         0 $cgi = $options->{cgi};
145             }
146             else {
147             # this is the normal path for App-Context execution, where the Request::CGI
148             # is responsible for reading its environment
149 1         8 $cgi = CGI->new();
150 1 50       4957 $options->{cgi} = $cgi if (defined $options);
151             }
152             }
153              
154             # when the "debug_request" is "record", save the CGI vars
155 1 50       6 if ($record) {
156 0         0 $file = "$app.vars";
157 0 0       0 if (open(App::FILE, "> $file")) {
158 0         0 $cgi->save(*App::FILE); # Save vars to debug file
159 0         0 close(App::FILE);
160             }
161             }
162              
163             #################################################################
164             # LANGUAGE
165             #################################################################
166              
167 1         2 my $lang = "en_us"; # default
168 1 50       18 if (defined $ENV{HTTP_ACCEPT_LANGUAGE}) {
    50          
169 0         0 $lang = lc($ENV{HTTP_ACCEPT_LANGUAGE});
170 0         0 $lang =~ s/ *,.*//;
171 0         0 $lang =~ s/-/_/g;
172             }
173             elsif ($options->{lang}) {
174 0         0 $lang = lc($options->{lang});
175 0         0 $lang =~ s/ *,.*//;
176 0         0 $lang =~ s/-/_/g;
177             }
178 1         3 $self->{lang} = $lang; # TODO: do something with the $lang ...
179              
180 1         3 $self->{cgi} = $cgi;
181 1 50       8 &App::sub_exit() if ($App::trace);
182             }
183              
184             #############################################################################
185             # PUBLIC METHODS
186             #############################################################################
187              
188             =head1 Public Methods
189              
190             =cut
191              
192             #############################################################################
193             # get_session_id()
194             #############################################################################
195              
196             =head2 get_session_id()
197              
198             The get_session_id() method returns the session_id in the request.
199              
200             * Signature: $session_id = $request->get_session_id();
201             * Param: void
202             * Return: $session_id string
203             * Throws:
204             * Since: 0.01
205              
206             Sample Usage:
207              
208             $session_id = $request->get_session_id();
209              
210             =cut
211              
212             sub get_session_id {
213 0 0   0 1   &App::sub_entry if ($App::trace);
214 0           my $self = shift;
215 0           my $session_id = $self->{cgi}->param("session_id");
216 0 0         &App::sub_exit($session_id) if ($App::trace);
217 0           return($session_id);
218             }
219              
220             #############################################################################
221             # get_events()
222             #############################################################################
223              
224             =head2 get_events()
225              
226             The get_events() method analyzes an HTTP request and returns the events
227             within it which should be executed.
228              
229             It is called primarily from the event loop handler, dispatch_events().
230             However, it might also be called from external software if that code manages
231             the event loop itself. i.e. it instantiates the CGI object outside of
232             the Context and passes it in, never calling dispatch_events().
233              
234             * Signature: $request->get_events()
235             * Signature: $request->get_events($cgi)
236             * Param: $cgi (CGI)
237             * Return: void
238             * Throws: App::Exception
239             * Since: 0.01
240              
241             Sample Usage:
242              
243             $request->get_events();
244              
245             =cut
246              
247             sub get_events {
248 0 0   0 1   &App::sub_entry if ($App::trace);
249 0           my ($self, $cgi) = @_;
250              
251 0 0         if (!defined $cgi) {
    0          
252 0           $cgi = $self->{cgi};
253             }
254             elsif (!defined $self->{cgi}) {
255 0           $self->{cgi} = $cgi;
256             }
257 0           my $context = $self->{context};
258 0           my $options = $context->{options};
259              
260 0 0 0       $context->dbgprint("Request::CGI->get_events() cgi=$cgi")
261             if ($App::DEBUG && $context->dbg(1));
262              
263 0           my (@events);
264              
265 0 0         if (defined $cgi) {
266 0           my ($service, $name, $method, $args, $init_args, $temp);
267 0   0       my $request_method = $cgi->request_method() || "GET";
268              
269 0 0 0       if ($request_method eq "GET" || $request_method eq "POST") {
270 0           my $path_info = $ENV{PATH_INFO};
271 0           $path_info =~ s!/$!!; # delete trailing "/"
272 0           my $options = $context->options();
273 0           my $app = $options->{app};
274 0 0 0       if ($path_info && $app) {
275             # this is because App::Options uses the first leg of the PATH_INFO
276             # to set the {app} if the program name is the generic "app"
277 0           $path_info =~ s!/$app!!; # delete leading $app prefix
278             }
279              
280             # Note: the is found in another location when it is needed
281             # here, we simply need to delete the trailing : or .
282 0           $path_info =~ s!:[a-zA-Z0-9_]+$!!; # delete trailing :
283 0           $path_info =~ s!\.(html|xml|yaml|csv|pdf|perl|json)$!!; # delete trailing .
284              
285 0 0         if ($path_info =~ s!^/([A-Z][A-Za-z0-9]*)/!/!) {
286 0           $service = $1;
287             }
288             else {
289 0           $service = "SessionObject";
290             }
291              
292 0           $method = "";
293 0           $args = "";
294 0           $init_args = "";
295 0 0         if ($request_method eq "GET") {
296             # get PATH_INFO and see if an event is embedded there
297 0 0         if ($path_info =~ s!\.([a-zA-Z0-9_]+)\(([^\(\)]*)\)$!!) {
    0          
298 0           $method = $1;
299 0           $args = $2;
300             }
301             elsif ($path_info =~ s!\.([a-zA-Z0-9_]+)$!!) {
302 0           $method = $1;
303 0           $args = "";
304             }
305             }
306             else {
307 0           $path_info =~ s!\.([a-zA-Z0-9_]+)\(([^\(\)]*)\)$!!;
308             }
309              
310 0 0         if ($path_info =~ s!^/([a-zA-Z_][a-zA-Z0-9._-]*)\((.*)\)$!!) {
    0          
    0          
311 0           $name = $1;
312 0           $init_args = "{$2}";
313             }
314             elsif ($path_info =~ m!^/([a-zA-Z_][a-zA-Z0-9._-]*)$!) {
315 0           $name = $1;
316             }
317             elsif ($options->{default_cname}) {
318 0           $name = $options->{default_cname};
319             }
320             else {
321 0           $name = $app;
322             }
323              
324             # override PATH_INFO with CGI variables
325 0           $temp = $cgi->param("service");
326 0 0         $service = $temp if ($temp);
327 0           $temp = $cgi->param("name");
328 0 0         $name = $temp if ($temp);
329 0           $temp = $cgi->param("method");
330 0 0         $method = $temp if ($temp);
331 0           $temp = $cgi->param("init_args");
332 0 0         $init_args = $temp if ($temp);
333              
334 0           my $content = "";
335 0 0 0       if (!$method && $request_method eq "POST" && $cgi->{POSTDATA} && ref($cgi->{POSTDATA}) eq "ARRAY" && $#{$cgi->{POSTDATA}} > -1) {
  0   0        
      0        
      0        
336 0           $content = $cgi->{POSTDATA}[0];
337 0 0         if ($content =~ /^\s*(<\?xml[^<>]*\?>)?\s*<([A-Za-z_]+)/s) {
338 0           $method = $2;
339 0           $args = [ $content ];
340             }
341             }
342              
343 0 0 0       if ($init_args && $options->{open_widget_urls}) {
344 0           my $ser = $context->serializer("one_line", class => "App::Serializer::OneLine");
345 0           my $iargs = $ser->deserialize($init_args);
346 0           my $w = $context->widget($name, %$iargs);
347             }
348 0           my $permissions = $context->_so_get($name, "permissions");
349              
350 0 0 0       if ($service && $name && $method) {
    0 0        
      0        
351 0           $temp = $cgi->param("args");
352 0 0 0       if ($temp) {
    0          
353 0           $args = $temp;
354 0 0         if ($args =~ /^\s*$/) {
355 0           $args = [];
356             }
357             else {
358 0   0       my $argstype = $cgi->param("argstype") || $self->get_returntype();
359 0           my ($ser);
360 0 0         if ($argstype) {
361 0           $ser = $context->serializer($argstype);
362             }
363             else {
364 0           $ser = $context->serializer("one_line", class => "App::Serializer::OneLine");
365             }
366 0           $args = $ser->deserialize($args);
367             }
368             }
369             elsif (defined $args && !ref($args)) {
370 0 0         if ($args =~ /^\s*$/) {
371 0           $args = [];
372             }
373             else {
374 0           my $ser = $context->serializer("one_line", class => "App::Serializer::OneLine");
375 0           $args = $ser->deserialize($args);
376             }
377             }
378 0 0 0       if (!$options->{open_widget_urls} && (!$permissions || !$permissions->{$method})) {
      0        
379 0           die "Not permitted to perform the [$method] method on the [$name] widget\n";
380             }
381 0           push(@events, [ $service, $name, $method, $args, 1 ]);
382             }
383             elsif ($service && $name) {
384 0 0 0       if (!$options->{open_widget_urls} && (!$permissions || !$permissions->{view})) {
      0        
385 0           die "Not permitted to view widget [$name] from the browser\n";
386             }
387 0           $context->so_get("default","ctype",$service,1);
388 0           $context->so_get("default","cname",$name,1);
389             }
390             }
391              
392             ##########################################################
393             # For each CGI variable, do the appropriate thing
394             # 1. "app.event.*" variable is an event and gets handled last
395             # 2. "app.*" variable is a "multi-level hash key" under $context
396             # 3. "name{m}[1]" variable is a "multi-level hash key" under $context->{session_object}{$name}
397             # 4. "name" variable is a "multi-level hash key"
398             ##########################################################
399 0           my (@eventvars, $var, @values, @tmp, $values, $value, $mlhashkey);
400 0           @eventvars = ();
401 0           foreach $var ($cgi->param()) {
402 0 0         if ($var =~ /^app\.event/) {
    0          
403 0           push(@eventvars, $var);
404             }
405             elsif ($var =~ /^app\.session/) {
406             # do nothing.
407             # these vars are used in the Session restore() to restore state.
408             }
409             else {
410 0           @values = $cgi->param($var);
411 0 0         if ($#values > 0) {
412 0           @tmp = ();
413 0           foreach $value (@values) {
414 0 0         if ($value eq "{:delete:}") {
415 0           my $delvar = $var;
416 0           $delvar =~ s/\[\]$//;
417             # $context->so_delete($name, $delvar); # ?!? 2005-06-01: SPA Removed
418 0           $context->so_delete($delvar);
419             }
420             else {
421 0           push(@tmp, $value);
422             }
423             }
424 0           @values = @tmp;
425             }
426              
427 0 0         if ($var =~ s/\[\]$//) {
    0          
    0          
428 0           $value = [ @values ];
429             }
430             elsif ($#values == -1) {
431 0           $value = "";
432             }
433             elsif ($#values == 0) {
434 0           $value = $values[0];
435             }
436             else {
437 0           $value = join(",",@values);
438             }
439              
440 0 0 0       $context->dbgprint("Request::CGI->get_events() var=[$var] value=[$value]")
441             if ($App::DEBUG && $context->dbg(1));
442              
443 0 0 0       if ($var =~ /[\[\]\{\}\.]/) {
    0 0        
    0 0        
      0        
      0        
444 0           $context->so_set($var, "", $value);
445             }
446             elsif ($var eq "service" || $var eq "name" || $var eq "init_args" || $var eq "method" ||
447             $var eq "args" || $var eq "returntype") {
448             # this has already been done
449             # $context->so_set("default", $var, $value);
450             }
451             # Autoattribute vars: e.g. "width" (an attribute of session_object named in request)
452             elsif ($name) {
453             # $context->so_set($name, $var, $value);
454 0           $context->so_set($var, undef, $value);
455             }
456             # Simple vars: e.g. "width" (gets dumped in the "default" session_object)
457             else {
458             # $context->so_set("default", $var, $value);
459 0           $context->so_set($var, undef $value);
460             }
461             }
462             }
463              
464 0           my ($key, $fullkey, $arg, @args, $event, %x, %y, $x, $y);
465 0           foreach $key (@eventvars) {
466              
467             # These events come from type controls
468             # The format is name="app.event.{session_objectName}.{event}(args)"
469             # Note: this format is important because the "value" is needed for display purposes
470              
471 0 0 0       $context->dbgprint("Request::CGI->get_events() eventvar=[$key]")
472             if ($App::DEBUG && $context->dbg(1));
473              
474 0 0         if ($key =~ /^app\.event\./) {
    0          
475              
476 0           $args = "";
477 0           @args = ();
478 0 0         if ($key =~ /\((.*)\)/) { # look for anything inside parentheses
479 0           $args = $1;
480             }
481 0 0         if ($args eq "") {
    0          
482             # do nothing, @args = ()
483             }
484             elsif ($args =~ /\{/) {
485 0           foreach $arg (split(/ *, */,$args)) {
486 0 0         if ($arg =~ /^\{(.*)\}$/) {
487 0           push(@args, $context->so_get($1));
488             }
489             else {
490 0           push(@args, $arg);
491             }
492             }
493             }
494             else {
495 0 0         @args = split(/ *, */,$args) if ($args ne "");
496             }
497              
498             # returns e.g. joe.x=20 joe.y=35
499             # these two variables get turned into one event with $x, $y added to the end of the @args
500 0           $fullkey = $key;
501 0 0         if ($key =~ /^(.*)\.x$/) {
    0          
502 0           $key = $1;
503 0           $x{$key} = $cgi->param($fullkey);
504 0 0         next if (!defined $y{$key});
505 0           push(@args, $x{$key}); # tack $x, $y coordinates on at the end
506 0           push(@args, $y{$key});
507             }
508             elsif ($key =~ /^(.*)\.y$/) {
509 0           $key = $1;
510 0           $y{$key} = $cgi->param($fullkey);
511 0 0         next if (!defined $x{$key});
512 0           push(@args, $x{$key}); # tack $x, $y coordinates on at the end
513 0           push(@args, $y{$key});
514             }
515             else {
516 0           push(@args, $cgi->param($key)); # tack the label on at the end
517             }
518              
519 0           $key =~ s/^app\.event\.//; # get rid of prefix
520 0           $key =~ s/\(.*//; # get rid of args
521              
522 0 0 0       $context->dbgprint("Request::CGI->get_events() key=[$key] args=[@args]")
523             if ($App::DEBUG && $context->dbg(1));
524              
525 0 0         if ($key =~ /^([^()]+)\.([a-zA-Z0-9_-]+)$/) {
526 0           $name = $1;
527 0           $event = $2;
528              
529 0           push(@events, [ "SessionObject", $name, $event, [ @args ] ]);
530              
531             #if ($context->session_object_exists($name)) {
532             # $context->dbgprint("Request::CGI->get_events() handle_event($name, $event, @args) [button]")
533             # if ($App::DEBUG && $context->dbg(1));
534             # $context->session_object($name)->handle_event($name, $event, @args);
535             #}
536             #else {
537             # my ($parent_name);
538             # $parent_name = $name;
539             # $context->dbgprint("Request::CGI->get_events() $name doesn't exist, trying parents...")
540             # if ($App::DEBUG && $context->dbg(1));
541             # while ($parent_name =~ s/\.[^\.]+$//) {
542             # if ($context->session_object_exists($parent_name)) {
543             # $context->dbgprint("Request::CGI->get_events() handle_event($name, $event, @args) [button]")
544             # if ($App::DEBUG && $context->dbg(1));
545             # $context->session_object($parent_name)->handle_event($name, $event, @args);
546             # last;
547             # }
548             # $context->dbgprint("Request::CGI->get_events() $parent_name doesn't exist")
549             # if ($App::DEBUG && $context->dbg(1));
550             # }
551             #}
552             }
553             }
554             elsif ($key eq "app.event") {
555              
556             # These events come from type controls
557             # They are basically call-backs so that the session_object could clean up something before being viewed
558             # The format is name="app.event" value="{session_objectName}.{event}"
559 0           foreach $values ($cgi->param($key)) {
560 0           foreach $value (split(/;/,$values)) {
561 0 0         if ($value =~ /^([^()]+)\.([a-zA-Z0-9_-]+)/) {
562 0           $name = $1;
563 0           $event = $2;
564 0           $args = "";
565 0           @args = ();
566 0 0         if ($value =~ /\((.*)\)/) { # look for anything inside parentheses
567 0           $args = $1;
568             }
569 0 0         @args = split(/ *, */,$args) if ($args ne "");
570 0           push(@events, [ "SessionObject", $name, $event, [ @args ] ]);
571             }
572             }
573             }
574             }
575             }
576              
577 0 0 0       $context->dbgprint("Request->get_events(): $service($name).$method($args)")
578             if ($App::DEBUG && $context->dbg(1));
579             }
580              
581 0 0         &App::sub_exit(\@events) if ($App::trace);
582 0           return(\@events);
583             }
584              
585             sub get_returntype {
586 0 0   0 0   &App::sub_entry if ($App::trace);
587 0           my ($self, $cgi) = @_;
588              
589 0           my $returntype = $self->{returntype};
590 0 0         if (!$returntype) {
591 0 0         if (!defined $cgi) {
    0          
592 0           $cgi = $self->{cgi};
593             }
594             elsif (!defined $self->{cgi}) {
595 0           $self->{cgi} = $cgi;
596             }
597 0 0         if ($cgi) {
598 0           $returntype = $cgi->param("returntype");
599             }
600 0           my $context = $self->{context};
601 0           my $path_info = $ENV{PATH_INFO};
602 0 0         if ($path_info =~ /:([a-zA-Z0-9_]+)$/) {
    0          
603 0           $returntype = $1;
604             }
605             elsif ($path_info =~ m!\.(html|xml|yaml|csv|pdf|perl|json)$!) {
606 0           $returntype = $1;
607             }
608 0           $self->{returntype} = $returntype;
609             }
610 0 0         &App::sub_exit($returntype) if ($App::trace);
611 0           return($returntype);
612             }
613              
614             #############################################################################
615             # user()
616             #############################################################################
617              
618             =head2 user()
619              
620             The user() method returns the username of the authenticated user.
621             The special name, "guest", refers to the unauthenticated (anonymous) user.
622              
623             * Signature: $username = $request->user();
624             * Param: void
625             * Return: string
626             * Throws:
627             * Since: 0.01
628              
629             Sample Usage:
630              
631             $username = $request->user();
632              
633             =cut
634              
635             sub user {
636 0 0   0 1   &App::sub_entry if ($App::trace);
637 0           my $self = shift;
638 0   0       my $user = $ENV{REMOTE_USER} || "guest";
639 0 0         &App::sub_exit($user) if ($App::trace);
640 0           return ($user);
641             }
642              
643             #############################################################################
644             # header()
645             #############################################################################
646              
647             =head2 header()
648              
649             The header() method returns the specified HTTP header from the request.
650              
651             * Signature: $header_value = $request->header($header_name);
652             * Param: $header_name string
653             * Return: $header_value string
654             * Throws:
655             * Since: 0.01
656              
657             Sample Usage:
658              
659             $header_value = $request->header("Accept-Encoding");
660              
661             =cut
662              
663             sub header {
664 0 0   0 1   &App::sub_entry if ($App::trace);
665 0           my ($self, $header_name) = @_;
666 0           my $header = $self->{cgi}->http($header_name);
667 0 0         &App::sub_exit($header) if ($App::trace);
668 0           return($header);
669             }
670              
671             #############################################################################
672             # url()
673             #############################################################################
674              
675             =head2 url()
676              
677             The url() method returns information about the request url.
678              
679             * Signature: $url = $request->url();
680             * Return: $url string
681              
682             Sample Usage:
683              
684             $url = $request->url();
685              
686             =cut
687              
688             sub url {
689 0 0   0 1   &App::sub_entry if ($App::trace);
690 0           my ($self) = @_;
691 0           my ($url);
692 0           my $cgi = $self->{cgi};
693 0 0         if ($cgi) {
694 0           my $context = $self->{context};
695 0           my $options = $context->{options};
696              
697 0           my %standard_keep_param = ( u => 1, p => 1, eu => 1, eu_normal => 1 );
698              
699 0           my (%additional_keep_param);
700 0 0         if ($options->{"app.Request.keep_url_params"}) {
701 0           %additional_keep_param = map { $_ => 1 } split(/[ ,]+/, $options->{"app.Request.keep_url_params"});
  0            
702             }
703              
704 0           my %keep_param = (%standard_keep_param, %additional_keep_param);
705              
706 0           my $query_string = "";
707 0           foreach my $param ($cgi->url_param()) {
708 0 0         if ($keep_param{$param}) {
709 0 0         $query_string .= ($query_string ? "&" : "?") . "$param=" . $cgi->url_param($param);
710             }
711             }
712              
713 0           $url = $cgi->url(-path_info=>1) . $query_string;
714             }
715 0 0         &App::sub_exit($url) if ($App::trace);
716 0           return($url);
717             }
718              
719             1;
720