#!/usr/bin/perl # # GET_to_POST # # AUTHOR: # Dan Harkless # # COPYRIGHT: # This file is Copyright (C) 2016 by Dan Harkless, and is released under the # GNU General Public License . # # DESCRIPTION: # Converts a GET request to this script into a POST request to the specified # URL, redirecting automatically via JavaScript, if desired. # # PARAMETERS: # _GET_to_POST_charset # The character set to be specified in the HTTP header and a # tag. If the parameter is not present, we default to # ISO-8859-1. To prevent a charset from being specified in the http-equiv, # set this to the empty value ("..._GET_to_POST_charset=&..."). # # _GET_to_POST_lang # The language to be specified on the tag. If the parameter is not # present, we default to "en". To prevent the "lang" attribute from being # specified, set this to the empty value ("..._GET_to_POST_lang=&..."). # # _GET_to_POST_manual # Usually GET_to_POST generates a page that automatically resubmits the # request, in POST form, using JavaScript. Thus it acts like an HTTP # redirect. If this parameter is set to nonzero, however, the JavaScript # will not be generated, and the user of the generated page will have to # manually hit the submit button to POST the request. # # _GET_to_POST_submit_name # If this is not specified, the submit button will be called "Submit". If # GET_to_POST doesn't seem to be working for you, try setting the name of # the submit button to be the same as the button on the page that usually # calls the script (the one that you're avoiding because you want to use a # GET URL). # # If the name contains a space, the JavaScript that automatically presses # the button will not work. # # _GET_to_POST_submit_value # If not set, the value attribute of the button will be the same as the name # attribute. # # _GET_to_POST_unhide # Usually the parameters specified to GET_to_POST (not starting with # "_GET_to_POST_") are converted to hidden inputs in the form it generates. # If you'd like the parameters to be converted to visible and editable text # input fields instead, set _GET_to_POST_unhide to nonzero. This is mostly # of use if you're also using _GET_to_POST_manual. # # _GET_to_POST_URL # This is the only required parameter. It specifies the URL of the script # to POST to. # # # Any other parameters will be passed as POST parameters to the script # specified by _GET_to_POST_URL. # # EXAMPLE: # http://site/GET_to_POST.cgi?_GET_to_POST_URL=http://www.dnsrbl.com/lookupser # ver.jsp&server=127.0.0.2 # # DATE MODIFICATION # ========== ================================================================== # 2016-07-04 Use HTML 4.01 Transitional instead of cur. CGI.pm default, XHTML. # 2016-07-04 Added _GET_to_POST_charset and _GET_to_POST_lang parameters. # 2016-07-04 Assume Perl 5.6.0+; 'use warnings' rather than -w on shebang line # so we only output warnings in this script, not in modules we use. # 2008-09-02 "use English qw(-no_match_vars)": avoid regex performance penalty. # 2006-09-25 Forgot to escapeHTML() the URL when _GET_to_POST_unhide=1. And # when _GET_to_POST_manual=0, _GET_to_POST_submit_name needs to be # restricted to the character set [-_:A-Za-z0-9], although # preventing XSS with this restriction comes at the expense of not # being able to POST to forms using illegal (but browser-accepted) # submit names. We'll allow a broader character set when # _GET_to_POST_manual=1 so we can still support such forms. # 2004-03-16 Only allow redirection to http: or https: URLs, to prevent the use # of javascript:, vbscript:, data:, or other such schemes, in case # anyone is using this on a site with cookies. Note that if you use # this with CGI.pm 3.04 or earlier, there's still an XSS hole # because start_form() fails to HTML-encode its parameters, contrary # to the docs. I've reported this to Lincoln Stein -- it's fixed in # 3.05. # 2003-03-15 Original. ## Modules used ################################################################ use CGI; use English qw(-no_match_vars); # allow use of names like @ARG rather than @_ use warnings; # output warnings for this script but not for modules used ## Subroutines ################################################################# sub die_pre_html_start { print $cgi->header(-type => $content_type); print $cgi->start_html(@start_html_params, -title => "ERROR from $program_name_no_path"); print $cgi->h1("ERROR from $program_name_no_path:"); print $cgi->p(@ARG); print $cgi->end_html, "\n"; exit 1; } sub die_post_html_start { print $cgi->h1("ERROR from $program_name_no_path:"); print $cgi->p(@ARG); print $cgi->end_html, "\n"; exit 1; } sub html_escape { # This is a copy & paste of CGI.pm's internal escapeHTML() routine except # without the here-meaningless 'dontescape' checking. my $toencode = shift; return undef unless defined($toencode); $toencode=~s/&/&/g; $toencode=~s/\"/"/g; # would have to be '"' if HTML 3.2 were used $toencode=~s/>/>/g; $toencode=~s/ "GET_to_POST, by Dan Harkless --" . " http://harkless.org/dan/software/"}; $OUTPUT_AUTOFLUSH = 1; # let the browser render as dynamically as possible ($program_name_no_path = $PROGRAM_NAME) =~ s<.*/><>; @start_html_params = (-dtd => "-//W3C//DTD HTML 4.01 Transitional//EN", -meta => $meta_tags); $cgi = new CGI; if ($cgi->cgi_error()) { # Surprisingly, current browsers (as of 2003) don't respond properly to HTTP # status code 413, so we'll generate an HTML error page rather than calling # $cgi->header($cgi->cgi_error()). die_pre_html_start($cgi->cgi_error()); } open(STDERR, ">&" . STDOUT); # duplicate stderr to stdout so we see errors ## Main ######################################################################## if (defined($cgi->param("_GET_to_POST_charset"))) { $charset = html_escape($cgi->param("_GET_to_POST_charset")); if ($charset) { $content_type = "text/html; charset=$charset"; } else { # Even though it's allowed per HTTP 1.1 to specify the Content-Type # without an explicit charset and have it default to ISO-8859-1, the # version of CGI.pm I'm using changes the below into an explicitly # defaulted "text/html; charset=ISO-8859-1" in the HTTP header (though # it comes through as-is in the ). $content_type = "text/html"; } } push @start_html_params, (-head => $cgi->meta({-http_equiv => "Content-Type", -content => $content_type})); if (defined($cgi->param("_GET_to_POST_lang"))) { $lang = $cgi->param("_GET_to_POST_lang"); } if ($lang) { push @start_html_params, (-lang => $lang); } if (not defined $cgi->param("_GET_to_POST_URL")) { die_pre_html_start("The _GET_to_POST_URL parameter must be set", " to the URL to POST to."); } if ($cgi->param("_GET_to_POST_URL") =~ /^([^:]+):/) { if ($1 ne "http" and $1 ne "https") { die_pre_html_start("If _GET_to_POST_URL specifies a", ' protocol, it must be either "http" or', ' "https". If "' . $cgi->escapeHTML($1) . '" was not intended as', ' a protocol specifier, you need to', ' double-URL-encode any colons in your', ' target URL as "%253A".'); } } if (defined $ENV{QUERY_STRING}) { # Prefer the original format of the query string, if the webserver tells us. $title = $program_name_no_path . "?" . $ENV{QUERY_STRING}; } else { # Use CGI.pm's representation, which may have additional URL-encoding. $title = $cgi->url(-query => 1, -relative => 1); } # The -title param. will be automatically HTML-encoded, so XSS is not a concern. push @start_html_params, (-title => $title); if (defined $cgi->param("_GET_to_POST_submit_name")) { $submit_name = $cgi->param("_GET_to_POST_submit_name"); if (not $cgi->param("_GET_to_POST_manual") and $submit_name !~ /^[-_:A-Za-z0-9]*$/) { die_post_html_start("_GET_to_POST_submit_name=" . $cgi->escapeHTML($submit_name) . ", which" . " contains characters outside the set" . " [-_:A-Za-z0-9]. This is not allowed" . " except when _GET_to_POST_manual=1."); } } else { $submit_name = "Submit"; } if (defined $cgi->param("_GET_to_POST_submit_value")) { $submit_value = $cgi->param("_GET_to_POST_submit_value"); } else { $submit_value = $submit_name; } $hit_submit_button_message = $cgi->p('Please hit the "' . $cgi->escapeHTML($submit_name) . '" button to continue.'); # HTTP header: print $cgi->header(-type => $content_type); # Start HTML: if ($cgi->param("_GET_to_POST_manual")) { print $cgi->start_html(@start_html_params), "\n"; print $hit_submit_button_message, "\n"; } else { # Have to use forms[0].$submit_name.click() rather than forms[0].submit() # because the latter won't include the submit button in the POST parameters, # and some scripts require it. print $cgi->start_html(@start_html_params, -onLoad => "document.forms[0].$submit_name.click();" ), "\n"; print $cgi->noscript($hit_submit_button_message), "\n"; } print $cgi->start_form(-action => $cgi->param("_GET_to_POST_URL"), -method => "POST"), "\n"; $max_param_value_len = 0; foreach $param_name ($cgi->param()) { if ($param_name !~ /^_GET_to_POST_/) { if ($cgi->param("_GET_to_POST_unhide")) { push @form_inputs, $param_name; if (length($cgi->param($param_name)) > $max_param_value_len) { $max_param_value_len = length($cgi->param($param_name)); } } else { print $cgi->hidden(-name => $param_name, -default => $cgi->param($param_name)), "\n"; } } } if ($cgi->param("_GET_to_POST_unhide")) { print " \n"; print " \n"; print " \n"; print " \n"; print " \n"; foreach $form_input (@form_inputs) { print " \n"; print " \n"; print " \n"; print " \n"; } print "
POST URL:", $cgi->escapeHTML($cgi->param("_GET_to_POST_URL")), "
" . $cgi->escapeHTML($form_input) . ":", $cgi->textfield(-name => $form_input, -size => $max_param_value_len, -default => $cgi->param($form_input)), "
\n"; } print "

", $cgi->submit(-name => $submit_name, -value => $submit_value), "

\n"; print $cgi->end_form(), "\n"; print $cgi->hr(); print "\n"; print " \n"; print " \n"; print " \n"; print " \n"; print "
Generator: \n", ' GET_to_POST', "", 'Validated HTML 4.01', " Transitional
\n"; print $cgi->end_html, "\n";