]> mj.ucw.cz Git - libucw.git/blob - lib/perl/CGI.pm
55e76e1c7fb4b56a79ad20148a80b10b47cb4f11
[libucw.git] / lib / perl / CGI.pm
1 #       Poor Man's CGI Module for Perl
2 #
3 #       (c) 2002--2007 Martin Mares <mj@ucw.cz>
4 #       Slightly modified by Tomas Valla <tom@ucw.cz>
5 #
6 #       This software may be freely distributed and used according to the terms
7 #       of the GNU Lesser General Public License.
8
9 package UCW::CGI;
10
11 use strict;
12 use warnings;
13
14 BEGIN {
15         # The somewhat hairy Perl export mechanism
16         use Exporter();
17         our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
18         $VERSION = 1.0;
19         @ISA = qw(Exporter);
20         @EXPORT = qw(&html_escape &url_escape &url_param_escape &self_ref &self_form);
21         @EXPORT_OK = qw();
22         %EXPORT_TAGS = ();
23 }
24
25 ### Escaping ###
26
27 sub url_escape($) {
28         my $x = shift @_;
29         $x =~ s/([^-\$_.!*'(),0-9A-Za-z\x80-\xff])/"%".unpack('H2',$1)/ge;
30         return $x;
31 }
32
33 sub url_param_escape($) {
34         my $x = shift @_;
35         $x = url_escape($x);
36         $x =~ s/%20/+/g;
37         return $x;
38 }
39
40 sub html_escape($) {
41         my $x = shift @_;
42         $x =~ s/&/&amp;/g;
43         $x =~ s/</&lt;/g;
44         $x =~ s/>/&gt;/g;
45         $x =~ s/"/&quot;/g;
46         return $x;
47 }
48
49 ### Analysing RFC 822 Style Headers ###
50
51 sub rfc822_prepare($) {
52         my $x = shift @_;
53         # Convert all %'s and backslash escapes to %xx escapes
54         $x =~ s/%/%25/g;
55         $x =~ s/\\(.)/"%".unpack("H2",$1)/ge;
56         # Remove all comments, beware, they can be nested (unterminated comments are closed at EOL automatically)
57         while ($x =~ s/^(("[^"]*"|[^"(])*(\([^)]*)*)(\([^()]*(\)|$))/$1 /) { }
58         # Remove quotes and escape dangerous characters inside (again closing at the end automatically)
59         $x =~ s{"([^"]*)("|$)}{my $z=$1; $z =~ s/([^0-9a-zA-Z%_-])/"%".unpack("H2",$1)/ge; $z;}ge;
60         # All control characters are properly escaped, tokens are clearly visible.
61         # Finally remove all unnecessary spaces.
62         $x =~ s/\s+/ /g;
63         $x =~ s/(^ | $)//g;
64         $x =~ s{\s*([()<>@,;:\\"/\[\]?=])\s*}{$1}g;
65         return $x;
66 }
67
68 sub rfc822_deescape($) {
69         my $x = shift @_;
70         $x =~ s/%(..)/pack("H2",$1)/ge;
71         return $x;
72 }
73
74 ### Reading of HTTP headers ###
75
76 sub http_get($) {
77         my $h = shift @_;
78         $h =~ tr/a-z-/A-Z_/;
79         return $ENV{"HTTP_$h"} || $ENV{"$h"};
80 }
81
82 ### Parsing of Arguments ###
83
84 our $arg_table;
85
86 sub parse_arg_string($) {
87         my ($s) = @_;
88         $s =~ s/\s+//;
89         foreach $_ (split /[&:]/,$s) {
90                 (/^([^=]+)=(.*)$/) or next;
91                 my $arg = $arg_table->{$1} or next;
92                 $_ = $2;
93                 s/\+/ /g;
94                 s/%(..)/pack("H2",$1)/eg;
95                 s/\r\n/\n/g;
96                 s/\r/\n/g;
97                 $arg->{'multiline'} || s/(\n|\t)/ /g;
98                 s/^\s+//;
99                 s/\s+$//;
100                 if (my $rx = $arg->{'check'}) {
101                         if (!/^$rx$/) { $_ = $arg->{'default'}; }
102                 }
103
104                 my $r = ref($arg->{'var'});
105                 if ($r eq 'SCALAR') {
106                         ${$arg->{'var'}} = $_;
107                 } elsif ($r eq 'ARRAY') {
108                         push @{$arg->{'var'}}, $_;
109                 }
110         }
111 }
112
113 sub parse_args($) {
114         $arg_table = shift @_;
115         foreach my $a (values %$arg_table) {
116                 my $r = ref($a->{'var'});
117                 defined($a->{'default'}) or $a->{'default'}="";
118                 if ($r eq 'SCALAR') {
119                         ${$a->{'var'}} = $a->{'default'};
120                 } elsif ($r eq 'ARRAY') {
121                         @{$a->{'var'}} = ();
122                 }
123         }
124         defined $ENV{"GATEWAY_INTERFACE"} or die "Not called as a CGI script";
125         my $method = $ENV{"REQUEST_METHOD"};
126         if ($method eq "GET") {
127                 parse_arg_string($ENV{"QUERY_STRING"});
128         } elsif ($method eq "POST") {
129                 if ($ENV{"CONTENT_TYPE"} =~ /^application\/x-www-form-urlencoded\b/i) {
130                         while (<STDIN>) {
131                                 chomp;
132                                 parse_arg_string($_);
133                         }
134                 } else {
135                         return "Unknown content type for POST data";
136                 }
137         } else {
138                 return "Unknown request method";
139         }
140 }
141
142 ### Generating Self-ref URL's ###
143
144 sub make_out_args($) {
145         my ($overrides) = @_;
146         my $out = {};
147         foreach my $name (keys %$arg_table) {
148                 my $arg = $arg_table->{$name};
149                 defined($arg->{'var'}) || next;
150                 defined($arg->{'pass'}) && !$arg->{'pass'} && !exists $overrides->{$name} && next;
151                 my $value;
152                 if (!defined($value = $overrides->{$name})) {
153                         if (exists $overrides->{$name}) {
154                                 $value = $arg->{'default'};
155                         } else {
156                                 $value = ${$arg->{'var'}};
157                         }
158                 }
159                 if ($value ne $arg->{'default'}) {
160                         $out->{$name} = $value;
161                 }
162         }
163         return $out;
164 }
165
166 sub self_ref(@) {
167         my %h = @_;
168         my $out = make_out_args(\%h);
169         return "?" . join(':', map { "$_=" . url_param_escape($out->{$_}) } sort keys %$out);
170 }
171
172 sub self_form(@) {
173         my %h = @_;
174         my $out = make_out_args(\%h);
175         return join('', map { "<input type=hidden name=$_ value='" . html_escape($out->{$_}) . "'>\n" } sort keys %$out);
176 }
177
178 ### Cookies
179
180 sub cookie_esc($) {
181         my $x = shift @_;
182         if ($x !~ /^[a-zA-Z0-9%]+$/) {
183                 $x =~ s/([\\\"])/\\$1/g;
184                 $x = "\"$x\"";
185         }
186         return $x;
187 }
188
189 sub set_cookie($$@) {
190         my $key = shift @_;
191         my $value = shift @_;
192         my %other = @_;
193         $other{'version'} = 1 unless defined $other{'version'};
194         print "Set-Cookie: $key=", cookie_esc($value);
195         foreach my $k (keys %other) {
196                 print ";$k=", cookie_esc($other{$k});
197         }
198         print "\n";
199 }
200
201 sub parse_cookies() {
202         my $h = http_get("Cookie") or return ();
203         my @cook = ();
204         while (my ($padding,$name,$val,$xx,$rest) = ($h =~ /\s*([,;]\s*)*([^ =]+)=([^ =,;\"]*|\"([^\"\\]|\\.)*\")(\s.*|;.*|$)/)) {
205                 if ($val =~ /^\"/) {
206                         $val =~ s/^\"//;
207                         $val =~ s/\"$//;
208                         $val =~ s/\\(.)/$1/g;
209                 }
210                 push @cook, $name, $val;
211                 $h = $rest;
212         }
213         return @cook;
214 }
215
216 1;  # OK