Annotation of capa/capa51/CapaTools/cgi-lib.pl, revision 1.2

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>