Annotation of capa/capa51/CapaTools/cgi-lib.pl, revision 1.1
1.1 ! albertel 1: # Perl Routines to Manipulate CGI input
! 2: # cgi-lib@pobox.com
! 3: # $Id: cgi-lib.pl,v 2.18 1999/02/23 08:16:43 brenner Exp $
! 4: #
! 5: # Copyright (c) 1993-1999 Steven E. Brenner
! 6: # Unpublished work.
! 7: # Permission granted to use and modify this library so long as the
! 8: # copyright above is maintained, modifications are documented, and
! 9: # credit is given for any use of the library.
! 10: #
! 11: # Thanks are due to many people for reporting bugs and suggestions
! 12:
! 13: # For more information, see:
! 14: # http://cgi-lib.stanford.edu/cgi-lib/
! 15:
! 16: $cgi_lib'version = sprintf("%d.%02d", q$Revision: 2.18 $ =~ /(\d+)\.(\d+)/);
! 17:
! 18:
! 19: # Parameters affecting cgi-lib behavior
! 20: # User-configurable parameters affecting file upload.
! 21: $cgi_lib'maxdata = 131072; # maximum bytes to accept via POST - 2^17
! 22: $cgi_lib'writefiles = "/tmp"; # directory to which to write files, or
! 23: # 0 if files should not be written
! 24: $cgi_lib'filepre = "cgi-lib"; # Prefix of file names, in directory above
! 25:
! 26: # Do not change the following parameters unless you have special reasons
! 27: $cgi_lib'bufsize = 8192; # default buffer size when reading multipart
! 28: $cgi_lib'maxbound = 100; # maximum boundary length to be encounterd
! 29: $cgi_lib'headerout = 0; # indicates whether the header has been printed
! 30:
! 31:
! 32: # ReadParse
! 33: # Reads in GET or POST data, converts it to unescaped text, and puts
! 34: # key/value pairs in %in, using "\0" to separate multiple selections
! 35:
! 36: # Returns >0 if there was input, 0 if there was no input
! 37: # undef indicates some failure.
! 38:
! 39: # Now that cgi scripts can be put in the normal file space, it is useful
! 40: # to combine both the form and the script in one place. If no parameters
! 41: # are given (i.e., ReadParse returns FALSE), then a form could be output.
! 42:
! 43: # If a reference to a hash is given, then the data will be stored in that
! 44: # hash, but the data from $in and @in will become inaccessable.
! 45: # If a variable-glob (e.g., *cgi_input) is the first parameter to ReadParse,
! 46: # information is stored there, rather than in $in, @in, and %in.
! 47: # Second, third, and fourth parameters fill associative arrays analagous to
! 48: # %in with data relevant to file uploads.
! 49:
! 50: # If no method is given, the script will process both command-line arguments
! 51: # of the form: name=value and any text that is in $ENV{'QUERY_STRING'}
! 52: # This is intended to aid debugging and may be changed in future releases
! 53:
! 54: sub ReadParse {
! 55: # Disable warnings as this code deliberately uses local and environment
! 56: # variables which are preset to undef (i.e., not explicitly initialized)
! 57: local ($perlwarn);
! 58: $perlwarn = $^W;
! 59: $^W = 0;
! 60:
! 61: local (*in) = shift if @_; # CGI input
! 62: local (*incfn, # Client's filename (may not be provided)
! 63: *inct, # Client's content-type (may not be provided)
! 64: *insfn) = @_; # Server's filename (for spooled files)
! 65: local ($len, $type, $meth, $errflag, $cmdflag, $got, $name);
! 66:
! 67: binmode(STDIN); # we need these for DOS-based systems
! 68: binmode(STDOUT); # and they shouldn't hurt anything else
! 69: binmode(STDERR);
! 70:
! 71: # Get several useful env variables
! 72: $type = $ENV{'CONTENT_TYPE'};
! 73: $len = $ENV{'CONTENT_LENGTH'};
! 74: $meth = $ENV{'REQUEST_METHOD'};
! 75:
! 76: if ($len > $cgi_lib'maxdata) { #'
! 77: &CgiDie("cgi-lib.pl: Request to receive too much data: $len bytes\n");
! 78: }
! 79:
! 80: if (!defined $meth || $meth eq '' || $meth eq 'GET' ||
! 81: $meth eq 'HEAD' ||
! 82: $type eq 'application/x-www-form-urlencoded') {
! 83: local ($key, $val, $i);
! 84:
! 85: # Read in text
! 86: if (!defined $meth || $meth eq '') {
! 87: $in = $ENV{'QUERY_STRING'};
! 88: $cmdflag = 1; # also use command-line options
! 89: } elsif($meth eq 'GET' || $meth eq 'HEAD') {
! 90: $in = $ENV{'QUERY_STRING'};
! 91: } elsif ($meth eq 'POST') {
! 92: if (($got = read(STDIN, $in, $len) != $len))
! 93: {$errflag="Short Read: wanted $len, got $got\n";};
! 94: } else {
! 95: &CgiDie("cgi-lib.pl: Unknown request method: $meth\n");
! 96: }
! 97:
! 98: @in = split(/[&;]/,$in);
! 99: push(@in, @ARGV) if $cmdflag; # add command-line parameters
! 100:
! 101: foreach $i (0 .. $#in) {
! 102: # Convert plus to space
! 103: $in[$i] =~ s/\+/ /g;
! 104:
! 105: # Split into key and value.
! 106: ($key, $val) = split(/=/,$in[$i],2); # splits on the first =.
! 107:
! 108: # Convert %XX from hex numbers to alphanumeric
! 109: $key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
! 110: $val =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
! 111:
! 112: # Associate key and value
! 113: $in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator
! 114: $in{$key} .= $val;
! 115: }
! 116:
! 117: } elsif ($ENV{'CONTENT_TYPE'} =~ m#^multipart/form-data#) {
! 118: # for efficiency, compile multipart code only if needed
! 119: $errflag = !(eval <<'END_MULTIPART');
! 120:
! 121: local ($buf, $boundary, $head, @heads, $cd, $ct, $fname, $ctype, $blen);
! 122: local ($bpos, $lpos, $left, $amt, $fn, $ser);
! 123: local ($bufsize, $maxbound, $writefiles) =
! 124: ($cgi_lib'bufsize, $cgi_lib'maxbound, $cgi_lib'writefiles);
! 125:
! 126:
! 127: # The following lines exist solely to eliminate spurious warning messages
! 128: $buf = '';
! 129:
! 130: ($boundary) = $type =~ /boundary="([^"]+)"/; #"; # find boundary
! 131: ($boundary) = $type =~ /boundary=(\S+)/ unless $boundary;
! 132: &CgiDie ("Boundary not provided: probably a bug in your server")
! 133: unless $boundary;
! 134: $boundary = "--" . $boundary;
! 135: $blen = length ($boundary);
! 136: $cgi_msg = "Begin multipart/form-data:: CONTENT_TYPE=[$type]\n";
! 137:
! 138: if ($ENV{'REQUEST_METHOD'} ne 'POST') {
! 139: &CgiDie("Invalid request method for multipart/form-data: $meth\n");
! 140: }
! 141:
! 142: if ($writefiles) {
! 143: local($me);
! 144: stat ($writefiles);
! 145: $writefiles = "/tmp" unless -d _ && -w _;
! 146: # ($me) = $0 =~ m#([^/]*)$#;
! 147: $writefiles .= "/$cgi_lib'filepre";
! 148: }
! 149:
! 150: # read in the data and split into parts:
! 151: # put headers in @in and data in %in
! 152: # General algorithm:
! 153: # There are two dividers: the border and the '\r\n\r\n' between
! 154: # header and body. Iterate between searching for these
! 155: # Retain a buffer of size(bufsize+maxbound); the latter part is
! 156: # to ensure that dividers don't get lost by wrapping between two bufs
! 157: # Look for a divider in the current batch. If not found, then
! 158: # save all of bufsize, move the maxbound extra buffer to the front of
! 159: # the buffer, and read in a new bufsize bytes. If a divider is found,
! 160: # save everything up to the divider. Then empty the buffer of everything
! 161: # up to the end of the divider. Refill buffer to bufsize+maxbound
! 162: # Note slightly odd organization. Code before BODY: really goes with
! 163: # code following HEAD:, but is put first to 'pre-fill' buffers. BODY:
! 164: # is placed before HEAD: because we first need to discard any 'preface,'
! 165: # which would be analagous to a body without a preceeding head.
! 166:
! 167: $left = $len;
! 168: PART: # find each part of the multi-part while reading data
! 169: while (1) {
! 170: die $@ if $errflag;
! 171:
! 172: $amt = ($left > $bufsize+$maxbound-length($buf)
! 173: ? $bufsize+$maxbound-length($buf): $left);
! 174: $errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
! 175: die "Short Read: wanted $amt, got $got\n" if $errflag;
! 176: $left -= $amt;
! 177:
! 178: $in{$name} .= "\0" if defined $in{$name};
! 179: $in{$name} .= $fn if $fn;
! 180:
! 181: $name=~/([-\w]+)/; # This allows $insfn{$name} to be untainted
! 182: if (defined $1) {
! 183: $insfn{$1} .= "\0" if defined $insfn{$1};
! 184: $insfn{$1} .= $fn if $fn;
! 185: }
! 186: $cgi_msg .= "Before BODY:: in{name}=[$in{$name}], name=[$name], fn=[$fn]\n";
! 187: $cgi_msg .= ":: amt=[$amt],buf(20)=[" . substr($buf,0,20) . "]\n";
! 188: $cgi_msg .= ":: buflen=[" . length($buf) . "]\n";
! 189:
! 190: BODY:
! 191: while (($bpos = index($buf, $boundary)) == -1) {
! 192: if ($left == 0 && $buf eq '') {
! 193: foreach $value (values %insfn) {
! 194: unlink(split("\0",$value));
! 195: }
! 196: &CgiDie("cgi-lib.pl: reached end of input while seeking boundary " .
! 197: "of multipart. Format of CGI input is wrong.\n");
! 198: }
! 199: die $@ if $errflag;
! 200: $cgi_msg .= "WITHIN BODY WHILE():: name = [$name], fn=[$fn]\n";
! 201: if ($name) { # if no $name, then it's the prologue -- discard
! 202: if ($fn) { print FILE substr($buf, 0, $bufsize); }
! 203: else { $in{$name} .= substr($buf, 0, $bufsize); }
! 204: }
! 205: $buf = substr($buf, $bufsize);
! 206: $amt = ($left > $bufsize ? $bufsize : $left); #$maxbound==length($buf);
! 207: $errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
! 208: die "Short Read: wanted $amt, got $got\n" if $errflag;
! 209: $left -= $amt;
! 210: }
! 211: $cgi_msg .= "WITHIN BODY:: name = [$name], fn=[$fn]\n";
! 212: $cgi_msg .= ":: buf(20)= [" . substr($buf,0,20) . "], bpos=[$bpos]\n";
! 213: if (defined $name) { # if no $name, then it's the prologue -- discard
! 214: if ($fn) { print FILE substr($buf, 0, $bpos-2); }
! 215: else { $in {$name} .= substr($buf, 0, $bpos-2); } # kill last \r\n
! 216: }
! 217: close (FILE);
! 218: last PART if substr($buf, $bpos + $blen, 2) eq "--";
! 219: substr($buf, 0, $bpos+$blen+2) = '';
! 220: $amt = ($left > $bufsize+$maxbound-length($buf)
! 221: ? $bufsize+$maxbound-length($buf) : $left);
! 222: $errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
! 223: die "Short Read: wanted $amt, got $got\n" if $errflag;
! 224: $left -= $amt;
! 225:
! 226: $cgi_msg .= "before HEAD:: buf(20)= [" . substr($buf,0,20) . "],amt=[$amt]\n";
! 227:
! 228: undef $head; undef $fn;
! 229: HEAD:
! 230: while (($lpos = index($buf, "\r\n\r\n")) == -1) {
! 231: if ($left == 0 && $buf eq '') {
! 232: foreach $value (values %insfn) {
! 233: unlink(split("\0",$value));
! 234: }
! 235: &CgiDie("cgi-lib: reached end of input while seeking end of " .
! 236: "headers. Format of CGI input is wrong.\n$buf");
! 237: }
! 238: die $@ if $errflag;
! 239: $head .= substr($buf, 0, $bufsize);
! 240: $buf = substr($buf, $bufsize);
! 241: $amt = ($left > $bufsize ? $bufsize : $left); #$maxbound==length($buf);
! 242: $errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
! 243: die "Short Read: wanted $amt, got $got\n" if $errflag;
! 244: $cgi_msg .= "HEAD WHILE(lpos=-1):: head=[$head],amt=[$amt]\n";
! 245: $left -= $amt;
! 246: }
! 247: $head .= substr($buf, 0, $lpos+2);
! 248: push (@in, $head);
! 249: @heads = split("\r\n", $head);
! 250: ($cd) = grep (/^\s*Content-Disposition:/i, @heads);
! 251: ($ct) = grep (/^\s*Content-Type:/i, @heads);
! 252:
! 253: ($name) = $cd =~ /\bname="([^"]+)"/i; #";
! 254: ($name) = $cd =~ /\bname=([^\s:;]+)/i unless defined $name;
! 255:
! 256: ($fname) = $cd =~ /\bfilename="([^"]*)"/i; #"; # filename can be null-str
! 257: ($fname) = $cd =~ /\bfilename=([^\s:;]+)/i unless defined $fname;
! 258: $incfn{$name} .= (defined $in{$name} ? "\0" : "") .
! 259: (defined $fname ? $fname : "");
! 260:
! 261: ($ctype) = $ct =~ /^\s*Content-type:\s*"([^"]+)"/i; #";
! 262: ($ctype) = $ct =~ /^\s*Content-Type:\s*([^\s:;]+)/i unless defined $ctype;
! 263: $inct{$name} .= (defined $in{$name} ? "\0" : "") . $ctype;
! 264:
! 265: $cgi_msg .= "Before Write:: Content-Type=[$ct]\n";
! 266: $cgi_msg .= "::Content-Disposition=[$cd]\n";
! 267: $cgi_msg .= "::name=[$name],in{name}=[$in{$name}],inct{name}=[$inct{$name}]\n";
! 268: $cgi_msg .= "::writefiles=[$writefiles],fname=[$fname]\n";
! 269: $cgi_msg .= "::head=[$head],heads=[@heads]\n";
! 270:
! 271: if ($writefiles && defined $fname) {
! 272: $ser++;
! 273: $fn = $writefiles . ".$$.$ser";
! 274: open (FILE, ">$fn") || &CgiDie("Couldn't open $fn\n");
! 275: binmode (FILE); # write files accurately
! 276: }
! 277: substr($buf, 0, $lpos+4) = '';
! 278: undef $fname;
! 279: undef $ctype;
! 280: }
! 281:
! 282: 1;
! 283: END_MULTIPART
! 284: if ($errflag) {
! 285: local ($errmsg, $value);
! 286: $errmsg = $@ || $errflag;
! 287: foreach $value (values %insfn) {
! 288: unlink(split("\0",$value));
! 289: }
! 290: &CgiDie($errmsg);
! 291: } else {
! 292: # everything's ok.
! 293: }
! 294: } else {
! 295: &CgiDie("cgi-lib.pl: Unknown Content-type: $ENV{'CONTENT_TYPE'}\n");
! 296: }
! 297:
! 298: # no-ops to avoid warnings
! 299: $insfn = $insfn;
! 300: $incfn = $incfn;
! 301: $inct = $inct;
! 302:
! 303: $^W = $perlwarn;
! 304:
! 305: return ($errflag ? undef : scalar(@in));
! 306: }
! 307:
! 308:
! 309: # PrintHeader
! 310: # Returns the magic line which tells WWW that we're an HTML document
! 311:
! 312: sub PrintHeader {
! 313: return "Content-type: text/html\n\n";
! 314: }
! 315:
! 316:
! 317: # HtmlTop
! 318: # Returns the <head> of a document and the beginning of the body
! 319: # with the title and a body <h1> header as specified by the parameter
! 320:
! 321: sub HtmlTop
! 322: {
! 323: local ($title) = @_;
! 324:
! 325: return <<END_OF_TEXT;
! 326: <html>
! 327: <head>
! 328: <title>$title</title>
! 329: </head>
! 330: <body>
! 331: <h1>$title</h1>
! 332: END_OF_TEXT
! 333: }
! 334:
! 335:
! 336: # HtmlBot
! 337: # Returns the </body>, </html> codes for the bottom of every HTML page
! 338:
! 339: sub HtmlBot
! 340: {
! 341: return "</body>\n</html>\n";
! 342: }
! 343:
! 344:
! 345: # SplitParam
! 346: # Splits a multi-valued parameter into a list of the constituent parameters
! 347:
! 348: sub SplitParam
! 349: {
! 350: local ($param) = @_;
! 351: local (@params) = split ("\0", $param);
! 352: return (wantarray ? @params : $params[0]);
! 353: }
! 354:
! 355:
! 356: # MethGet
! 357: # Return true if this cgi call was using the GET request, false otherwise
! 358:
! 359: sub MethGet {
! 360: return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "GET");
! 361: }
! 362:
! 363:
! 364: # MethPost
! 365: # Return true if this cgi call was using the POST request, false otherwise
! 366:
! 367: sub MethPost {
! 368: return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "POST");
! 369: }
! 370:
! 371:
! 372: # MyBaseUrl
! 373: # Returns the base URL to the script (i.e., no extra path or query string)
! 374: sub MyBaseUrl {
! 375: local ($ret, $perlwarn);
! 376: $perlwarn = $^W; $^W = 0;
! 377: $ret = 'http://' . $ENV{'SERVER_NAME'} .
! 378: ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') .
! 379: $ENV{'SCRIPT_NAME'};
! 380: $^W = $perlwarn;
! 381: return $ret;
! 382: }
! 383:
! 384:
! 385: # MyFullUrl
! 386: # Returns the full URL to the script (i.e., with extra path or query string)
! 387: sub MyFullUrl {
! 388: local ($ret, $perlwarn);
! 389: $perlwarn = $^W; $^W = 0;
! 390: $ret = 'http://' . $ENV{'SERVER_NAME'} .
! 391: ($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') .
! 392: $ENV{'SCRIPT_NAME'} . $ENV{'PATH_INFO'} .
! 393: (length ($ENV{'QUERY_STRING'}) ? "?$ENV{'QUERY_STRING'}" : '');
! 394: $^W = $perlwarn;
! 395: return $ret;
! 396: }
! 397:
! 398:
! 399: # MyURL
! 400: # Returns the base URL to the script (i.e., no extra path or query string)
! 401: # This is obsolete and will be removed in later versions
! 402: sub MyURL {
! 403: return &MyBaseUrl;
! 404: }
! 405:
! 406:
! 407: # CgiError
! 408: # Prints out an error message which which containes appropriate headers,
! 409: # markup, etcetera.
! 410: # Parameters:
! 411: # If no parameters, gives a generic error message
! 412: # Otherwise, the first parameter will be the title and the rest will
! 413: # be given as different paragraphs of the body
! 414:
! 415: sub CgiError {
! 416: local (@msg) = @_;
! 417: local ($i,$name);
! 418:
! 419: if (!@msg) {
! 420: $name = &MyFullUrl;
! 421: @msg = ("Error: script $name encountered fatal error\n");
! 422: };
! 423:
! 424: if (!$cgi_lib'headerout) { #')
! 425: print &PrintHeader;
! 426: print "<html>\n<head>\n<title>$msg[0]</title>\n</head>\n<body>\n";
! 427: }
! 428: print "<h1>$msg[0]</h1>\n";
! 429: foreach $i (1 .. $#msg) {
! 430: print "<p>$msg[$i]</p>\n";
! 431: }
! 432:
! 433: $cgi_lib'headerout++;
! 434: }
! 435:
! 436:
! 437: # CgiDie
! 438: # Identical to CgiError, but also quits with the passed error message.
! 439:
! 440: sub CgiDie {
! 441: local (@msg) = @_;
! 442: &CgiError (@msg);
! 443: die @msg;
! 444: }
! 445:
! 446:
! 447: # PrintVariables
! 448: # Nicely formats variables. Three calling options:
! 449: # A non-null associative array - prints the items in that array
! 450: # A type-glob - prints the items in the associated assoc array
! 451: # nothing - defaults to use %in
! 452: # Typical use: &PrintVariables()
! 453:
! 454: sub PrintVariables {
! 455: local (*in) = @_ if @_ == 1;
! 456: local (%in) = @_ if @_ > 1;
! 457: local ($out, $key, $output);
! 458:
! 459: $output = "\n<dl compact>\n";
! 460: foreach $key (sort keys(%in)) {
! 461: foreach (split("\0", $in{$key})) {
! 462: ($out = $_) =~ s/\n/<br>\n/g;
! 463: $output .= "<dt><b>$key</b>\n <dd>:<i>$out</i>:<br>\n";
! 464: }
! 465: }
! 466: $output .= "</dl>\n";
! 467:
! 468: return $output;
! 469: }
! 470:
! 471: # PrintEnv
! 472: # Nicely formats all environment variables and returns HTML string
! 473: sub PrintEnv {
! 474: &PrintVariables(*ENV);
! 475: }
! 476:
! 477:
! 478: # The following lines exist only to avoid warning messages
! 479: $cgi_lib'writefiles = $cgi_lib'writefiles;
! 480: $cgi_lib'bufsize = $cgi_lib'bufsize ;
! 481: $cgi_lib'maxbound = $cgi_lib'maxbound;
! 482: $cgi_lib'version = $cgi_lib'version;
! 483: $cgi_lib'filepre = $cgi_lib'filepre;
! 484:
! 485: 1; #return true
! 486:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>