1package Gantry::Engine::MP13; 2require Exporter; 3 4use strict; 5use Carp qw( croak ); 6 7use Apache::Constants qw( DECLINED OK REDIRECT HTTP_MOVED_PERMANENTLY 8 AUTH_REQUIRED SERVER_ERROR FORBIDDEN BAD_REQUEST ); 9use Apache::Request; 10use File::Basename; 11use Gantry::Conf; 12use Gantry::Utils::DBConnHelper::MP13; 13use CGI::Simple::Util (); 14 15use vars qw( @ISA @EXPORT ); 16 17############################################################ 18# Variables # 19############################################################ 20@ISA = qw( Exporter ); 21@EXPORT = qw( 22 apache_param_hash 23 apache_uf_param_hash 24 apache_request 25 base_server 26 cast_custom_error 27 consume_post_body 28 declined_response 29 dispatch_location 30 engine 31 engine_init 32 err_header_out 33 fish_config 34 fish_location 35 fish_method 36 fish_path_info 37 fish_uri 38 fish_user 39 get_arg_hash 40 get_auth_dbh 41 get_cached_config 42 get_config 43 get_dbh 44 get_post_body 45 header_in 46 header_out 47 hostname 48 is_connection_secure 49 is_status_declined 50 log_error 51 port 52 print_output 53 redirect_response 54 remote_ip 55 send_http_header 56 send_error_output 57 server_root 58 set_cached_config 59 set_content_type 60 set_no_cache 61 set_req_params 62 status_const 63 success_code 64 file_upload 65 url_encode 66 url_decode 67); 68 69############################################################ 70# Functions # 71############################################################ 72 73#------------------------------------------------- 74# $self->file_upload( param_name ) 75#------------------------------------------------- 76sub file_upload { 77 my( $self, $param ) = @_; 78 79 die "param required" if ! $param; 80 81 my $apr = $self->ap_req; 82 my $status = $apr->parse; 83 84 if ( $status ) { die "upload error: $status" }; 85 86 my $upload = $apr->upload( $param ); 87 88 my $filename = $upload->filename; 89 $filename =~ s/\\/\//g; 90 91 my( $name, $path, $suffix ) = fileparse( 92 $filename, 93 qr/\.(tar\.gz$|[^.]*)/ 94 ); 95 96 return( { 97 unique_key => time . rand( 6 ), 98 name => $name, 99 suffix => $suffix, 100 fullname => ( $name . $suffix ), 101 size => ( $upload->size || 0 ), 102 mime => $upload->type, 103 filehandle => $upload->fh, 104 } ); 105 106} 107 108#------------------------------------------------- 109# $self->log_error( error ) 110#------------------------------------------------- 111sub log_error { 112 my( $self, $msg ) = @_; 113 114 $self->r->log_error( $msg ); 115 116} 117 118#------------------------------------------------- 119# $self->cast_custom_error( error ) 120#------------------------------------------------- 121sub cast_custom_error { 122 my( $self, $error_page, $die_msg ) = @_; 123 124 $error_page ||= ''; 125 $die_msg ||= ''; 126 127 my $status = ( $self->status() ? $self->status() 128 : $self->status_const( 'BAD_REQUEST' ) ); 129 130 $self->r->log_error( $die_msg ) if defined $die_msg; 131 132 $self->r->custom_response( $status, $error_page ); 133 134 return( $status ); 135} 136 137#------------------------------------------------- 138# $self->apache_param_hash( $req ) 139#------------------------------------------------- 140sub apache_param_hash { 141 my( $self, $req ) = @_; 142 143 # If the application has specified that they want the unfiltered params 144 # by default, then make it happen. 145 if ($self->fish_config( 'unfiltered_params' ) && $self->fish_config( 'unfiltered_params' ) =~ /(1|on)/i) { 146 return $self->apache_uf_param_hash( $req ); 147 } 148 149 my $hash = {}; 150 my @param_names = $req->param; 151 152 foreach my $p ( @param_names ) { 153 my @values = $req->param( $p ); 154 155 # Replace angle brackets and quotes with named-entity equivalents. 156 $_ =~ s/</</g foreach @values; 157 $_ =~ s/>/>/g foreach @values; 158 $_ =~ s/"/"/g foreach @values; 159 $_ =~ s/'/'/g foreach @values; 160 161 # Trim leading / trailing whitespace. 162 $_ =~ s/^\s+//o foreach @values; 163 $_ =~ s/\s+$//o foreach @values; 164 165 $hash->{$p} = ( scalar @values == 1 ) ? shift @values : [ @values ]; 166 } 167 168 return( $hash ); 169 170} # end: apache_param_hash 171 172#------------------------------------------------- 173# $self->apache_uf_param_hash( $req ) 174#------------------------------------------------- 175sub apache_uf_param_hash { 176 my( $self, $req ) = @_; 177 178 my $hash = {}; 179 my @param_names = $req->param; 180 181 foreach my $p ( @param_names ) { 182 my @values = $req->param( $p ); 183 184 $hash->{$p} = ( scalar @values == 1 ) ? shift @values : [ @values ]; 185 } 186 187 return( $hash ); 188 189} # end: apache_uf_param_hash 190 191#------------------------------------------------- 192# $self->apache_request( ) 193#------------------------------------------------- 194sub apache_request { 195 my ( $self, $r ) = @_; 196 197 unless ( $self->{__AP_REQ__} ) { 198 $self->{__AP_REQ__} = Apache::Request->new( 199 $self->r, 200 POST_MAX => ( $self->fish_config( 'post_max' ) || '20000000' ) 201 ); 202 } 203 204 return $self->{__AP_REQ__}; 205} # end: apache_request 206 207#------------------------------------------------- 208# $self->base_server( $r ) 209#------------------------------------------------- 210sub base_server { 211 my( $self, $r ) = ( shift, shift ); 212 213 return( 214 $r ? $r->server->server_hostname 215 : $self->r->server->server_hostname ); 216 217} # end base_server 218 219#------------------------------------------------- 220# $self->hostname( $r ) 221#------------------------------------------------- 222sub hostname { 223 my( $self, $r ) = ( shift, shift ); 224 225 return( 226 $r ? $r->hostname 227 : $self->r->hostname ); 228 229} # end hostname 230 231#------------------------------------------------- 232# $self->consume_post_body( $r ) 233#------------------------------------------------- 234sub consume_post_body { 235 my $self = shift; 236 my $r = shift; 237 238 my $content_length = $r->headers_in->{'Content-length'}; 239 240 return unless $content_length; 241 242 $content_length = 1e6 if $content_length > 1e6; # limit to ~ 1Meg 243 244 my ( $content, $buffer ); 245 246 while ( $r->read( $buffer, $content_length ) ) { 247 $content .= $buffer; 248 } 249 250 $self->{__POST_BODY__} = $content; 251} 252 253#------------------------------------------------- 254# $self->declined_response( ) 255#------------------------------------------------- 256sub declined_response { 257 my $self = shift; 258 259 return $self->status_const( 'DECLINED' ); 260} # END declined_response 261 262#------------------------------------------------- 263# $self->dispatch_location( ) 264#------------------------------------------------- 265sub dispatch_location { 266 my $self = shift; 267 268 return $self->uri, $self->location; 269} # END dispatch_location 270 271#------------------------------------------------- 272# $self->engine 273#------------------------------------------------- 274sub engine { 275 return __PACKAGE__; 276 277} # end engine 278 279#------------------------------------------------- 280# $self->engine_init( ) 281#------------------------------------------------- 282sub engine_init { 283 my $self = shift; 284 my $r = shift; 285 286 $self->r( $r ); 287} # END engine_init 288 289#------------------------------------------------- 290# $self->err_header_out( $key, $value ) 291#------------------------------------------------- 292sub err_header_out { 293 my( $self, $k, $v ) = @_; 294 295 $self->r->err_headers_out->add( $k => $v ); 296 297} # end err_header_out 298 299#------------------------------------------------- 300# $self->fish_config( $param ) 301#------------------------------------------------- 302sub fish_config { 303 my ( $self, $param ) = @_; 304 305 # see if there is Gantry::Conf data 306 my $conf = $self->get_config(); 307 308 return $$conf{$param} if ( defined $conf and defined $$conf{$param} ); 309 310 # otherwise, use dir_config for traditional approach 311 return $self->r()->dir_config( $param ); 312 313} # END fish_config 314 315#------------------------------------------------- 316# $self->fish_location( ) 317#------------------------------------------------- 318sub fish_location { 319 my $self = shift; 320 321 return $self->r()->location; 322} # END fish_location 323 324#------------------------------------------------- 325# $self->fish_method( ) 326#------------------------------------------------- 327sub fish_method { 328 my $self = shift; 329 330 return $self->r()->method; 331} # END fish_method 332 333#------------------------------------------------- 334# $self->fish_path_info( ) 335#------------------------------------------------- 336sub fish_path_info { 337 my $self = shift; 338 339 return $self->r()->path_info; 340} # END fish_path_info 341 342#------------------------------------------------- 343# $self->fish_uri( ) 344#------------------------------------------------- 345sub fish_uri { 346 my $self = shift; 347 348 return $self->r()->uri; 349} # END fish_uri 350 351#------------------------------------------------- 352# $self->fish_user( ) 353#------------------------------------------------- 354sub fish_user { 355 my $self = shift; 356 357 return $self->user() || $self->r()->user; 358} # END fish_user 359 360#------------------------------------------------- 361# $self->get_arg_hash 362#------------------------------------------------- 363sub get_arg_hash { 364 my( $self, $r ) = @_; 365 366 my %args; 367 if ( $r ) { 368 %args = $r->args; 369 } 370 else { 371 %args = $self->r->args; 372 } 373 374 return wantarray ? %args : \%args; 375 376} # end get_arg_hash 377 378#------------------------------------------------- 379# $self->get_auth_dbh( ) 380#------------------------------------------------- 381sub get_auth_dbh { 382 return Gantry::Utils::DBConnHelper::MP13->get_auth_dbh; 383} 384 385#------------------------------------------------- 386# $self->get_config( ) 387#------------------------------------------------- 388sub get_config { 389 my ( $self ) = @_; 390 391 # see if there Gantry::Conf data 392 my $instance = $self->r()->dir_config( 'GantryConfInstance' ); 393 394 return unless defined $instance; 395 396 my $file = $self->r()->dir_config( 'GantryConfFile' ); 397 398 my $conf; 399 my $cached = 0; 400 my $location = ''; 401 402 eval { 403 $location = $self->location; 404 }; 405 406 $conf = $self->get_cached_config( $instance, $location ); 407 408 if ( defined $conf ) { 409 return $conf; 410 } 411 412 my $gantry_cache = 0; 413 my $gantry_cache_key = ''; 414 my $gantry_cache_hit = 0; 415 eval { 416 ++$gantry_cache if $self->cache_inited(); 417 }; 418 419 # are we using gantry cache ? 420 if ( $gantry_cache ) { 421 422 $self->cache_namespace('gantry'); 423 424 # blow the gantry conf cache when server starts 425 if ( $self->engine_cycle() == 1 ) { 426 427 eval { 428 foreach my $key ( @{ $self->cache_keys() } ) { 429 my @a = split( ':', $key ); 430 if ( $a[0] eq 'gantryconf' ) { 431 $self->cache_del( $key ); 432 } 433 } 434 }; 435 } 436 437 # build cache key 438 $gantry_cache_key = join( ':', 439 "gantryconf", 440 ( $self->namespace() || '' ), 441 $instance, 442 $location 443 ); 444 445 $conf = $self->cache_get( $gantry_cache_key ); 446 447 ++$gantry_cache_hit if defined $conf; 448 } 449 450 $conf ||= Gantry::Conf->retrieve( 451 { 452 instance => $instance, 453 config_file => $file, 454 location => $location 455 } 456 ); 457 458 if ( defined $conf ) { 459 $self->set_cached_config( $instance, $location, $conf ); 460 461 if ( $gantry_cache && ! $gantry_cache_hit ) { 462 $self->cache_set( $gantry_cache_key, $conf ); 463 } 464 } 465 466 return $conf; 467 468} # END get_config 469 470#------------------------------------------------- 471# $self->get_cached_config( $instance, $location ) 472#------------------------------------------------- 473sub get_cached_config { 474 my $self = shift; 475 my $instance = shift; 476 my $location = shift; 477 478 return $self->r()->pnotes( "conf_${instance}_${location}" ); 479} 480 481#------------------------------------------------- 482# $self->set_cached_config( $instance, $location, $conf ) 483#------------------------------------------------- 484sub set_cached_config { 485 my $self = shift; 486 my $instance = shift; 487 my $location = shift; 488 my $conf = shift; 489 490 $self->r()->pnotes( "conf_${instance}_${location}", $conf ); 491} 492 493#------------------------------------------------- 494# $self->get_dbh( ) 495#------------------------------------------------- 496sub get_dbh { 497 return Gantry::Utils::DBConnHelper::MP13->get_dbh; 498} 499 500#------------------------------------------------- 501# $self->get_post_body( ) 502#------------------------------------------------- 503sub get_post_body { 504 my $self = shift; 505 506 return $self->{__POST_BODY__}; 507} 508 509#------------------------------------------------- 510# $self->header_in( $key ) 511#------------------------------------------------- 512sub header_in { 513 my( $self, $key ) = @_; 514 515 return $self->r->header_in($key); 516 517} # end header_in 518 519#------------------------------------------------- 520# $self->header_out( $header_key, $header_value ) 521#------------------------------------------------- 522sub header_out { 523 my( $self, $k, $v ) = @_; 524 525 $self->r->header_out( $k => $v ); 526 527} # end header_out 528 529#------------------------------------------------- 530# $self->is_connection_secure() 531#------------------------------------------------- 532sub is_connection_secure { 533 my $self = shift; 534 535 return $self->r->subprocess_env('HTTPS') ? 1 : 0; 536} # END is_connection_secure 537 538#------------------------------------------------- 539# $self->is_status_declined( $status ) 540#------------------------------------------------- 541sub is_status_declined { 542 my $self = shift; 543 544 my $status = $self->status || ''; 545 546 return 1 if ( $status eq $self->status_const( 'DECLINED' ) ); 547} # END is_status_declined 548 549#------------------------------------------------- 550# $self->port( $r ) - NOT TIED TO API YET 551#------------------------------------------------- 552sub port { 553 my( $self, $r ) = ( shift, shift ); 554 555 return( '' ); 556 557} # end port 558 559#------------------------------------------------- 560# $self->print_output( ) 561#------------------------------------------------- 562sub print_output { 563 my $self = shift; 564 my $response = shift; 565 566 $self->r()->print( $response ); 567} # END print_output 568 569#------------------------------------------------- 570# $self->redirect_response( ) 571#------------------------------------------------- 572sub redirect_response { 573 my $self = shift; 574 575 return $self->status_const( 'REDIRECT' ); 576} # END redirect_response 577 578#------------------------------------------------- 579# $self->remote_ip( $r ) 580#------------------------------------------------- 581sub remote_ip { 582 my( $self, $r ) = @_; 583 584 return( 585 $r ? $r->connection->remote_ip 586 : $self->r->connection->remote_ip ); 587 588} # end remote_ip 589 590#------------------------------------------------- 591# $self->send_error_output( $@ ) 592#------------------------------------------------- 593sub send_error_output { 594 my $self = shift; 595 596 $self->do_error( $@ ); 597 return( $self->custom_error( $@ ) ); 598 599} # END send_error_output 600 601#------------------------------------------------- 602# $self->send_http_header( ) 603#------------------------------------------------- 604sub send_http_header { 605 my( $self ) = @_; 606 607 $self->r->send_http_header; 608 609} # end send_http_header 610 611#------------------------------------------------- 612# $self->server_root( $r ) - NOT TIED TO API YET 613#------------------------------------------------- 614sub server_root { 615 my( $self, $r ) = ( shift, shift ); 616 617 return(''); 618 619} # end server_root 620 621#------------------------------------------------- 622# $self->set_content_type( ) 623#------------------------------------------------- 624sub set_content_type { 625 my $self = shift; 626 627 $self->r()->content_type( $self->content_type ); 628} # END set_content_type 629 630#------------------------------------------------- 631# $self->set_no_cache( ) 632#------------------------------------------------- 633sub set_no_cache { 634 my $self = shift; 635 636 $self->r()->no_cache( 1 ) if ( $self->no_cache ); 637} # END set_no_cache 638 639#------------------------------------------------- 640# $self->set_req_params( ) 641#------------------------------------------------- 642sub set_req_params { 643 my $self = shift; 644 645 $self->ap_req( $self->apache_request( $self->r ) ); 646 $self->params( $self->apache_param_hash( $self->ap_req ) ); 647 $self->uf_params( $self->apache_uf_param_hash( $self->ap_req ) ); 648 649} # END set_req_params 650 651#------------------------------------------------- 652# $self->status_const( 'OK | DECLINED | REDIRECT' ) 653#------------------------------------------------- 654sub status_const { 655 my( $self, $status ) = @_; 656 657 # Upper case our status 658 $status = uc $status; 659 660 return BAD_REQUEST if $status eq 'BAD_REQUEST'; 661 return DECLINED if $status eq 'DECLINED'; 662 return OK if $status eq 'OK'; 663 return REDIRECT if $status eq 'REDIRECT'; 664 return HTTP_MOVED_PERMANENTLY 665 if $status eq 'MOVED_PERMANENTLY'; 666 return FORBIDDEN if $status eq 'FORBIDDEN'; 667 return AUTH_REQUIRED if $status eq 'AUTH_REQUIRED'; 668 return AUTH_REQUIRED if $status eq 'HTTP_UNAUTHORIZED'; 669 return SERVER_ERROR if $status eq 'SERVER_ERROR'; 670 671 die( "Undefined constant $status" ); 672 673} # end status_const 674 675#------------------------------------------------- 676# $self->success_code( ) 677#------------------------------------------------- 678sub success_code { 679 my $self = shift; 680 681 return $self->status_const( 'OK' ); 682} # END success_code 683 684#------------------------------------------------- 685# $self->url_encode( ) 686#------------------------------------------------- 687sub url_encode { 688 my $self = shift; 689 my $value = shift; 690 691 return CGI::Simple::Util::escape( $value ); 692} # END url_encode 693 694#------------------------------------------------- 695# $self->url_decode( ) 696#------------------------------------------------- 697sub url_decode { 698 my $self = shift; 699 my $value = shift; 700 701 return CGI::Simple::Util::unescape( $value ); 702} # END url_decode 703 704# EOF 7051; 706 707__END__ 708 709=head1 NAME 710 711Gantry::Engine::MP13 - mod_perl 1.0 plugin ( or mixin ) 712 713=head1 SYNOPSIS 714 715 use Gantry::Engine::MP13; 716 717 718=head1 DESCRIPTION 719 720This module is the binding between the Gantry framework and the mod_perl API. 721This particluar module contains the mod_perl 1.0 specific bindings. 722 723See mod_perl documentation for a more detailed description for some of these 724bindings. 725 726=head1 METHODS 727 728=over 4 729 730=item $self->apache_param_hash 731 732Return a hash reference to the apache request body parameters. 733 734=item $self->apache_uf_param_hash 735 736Return a hash reference to the apache request body parameters unfiltered. 737 738=item $self->apache_request 739 740Apache::Request is a subclass of the Apache class, which adds methods 741for parsing GET requests and POST requests where Content-type is one of 742application/x-www-form-urlencoded or multipart/form-data. See the 743libapreq(3) manpage for more details. 744 745=item $self->base_server 746 747Returns the physical server this connection came in 748on (main server or vhost): 749 750=item $self->hostname 751 752Returns the virtual server name 753 754=item $self->cast_custom_error 755 756Called by the handler in Gantry.pm when things go wrong. It receives 757html output and a death message. It logs the death message and sets 758the html output via the custom_response routine of the request object. 759Returns FORBIDDEN status code. 760 761=item $self->consume_post_body 762 763This must be used by a plugin at the pre_init phase. It takes all of the 764data from the body of the HTTP POST request, storing it for retrieval 765via C<get_post_body>. You cannot mix this with regular form handling. 766 767=item $self->declined_response 768 769Returns the proper numeric status code for DECLINED. 770 771=item dispatch_location 772 773The uri tail specific to this request. Returns: 774 775 $self->uri, $self->location 776 777Note that this is a two element list. 778 779=item $self->engine 780 781Returns the name of the engine, i.e. Gantry::Engine::MP13 782 783=item $self->engine_init 784 785Receives the request object and stores it in the site object. Returns 786nothing useful. 787 788=item $self->err_header_out 789 790The $r->err_headers_out method will return a %hash of server response 791headers. This can be used to initialize a perl hash, or one could use 792the $r->err_header_out() method (described below) to retrieve or set a 793specific header value directly 794 795See mod_perl docs. 796 797=item fish_config 798 799Pass this method the name of a conf parameter you need. Returns the 800value for the parameter. 801 802=item fish_location 803 804Returns the location for the current request. 805 806=item fish_method 807 808Returns the HTTP method of the current request. 809 810=item fish_path_info 811 812Returns the path info for the current request. 813 814=item fish_uri 815 816Returns the uri for the current request. 817 818=item fish_user 819 820Returns the currently logged-in user. 821 822=item $self->get_arg_hash 823 824 returns a hash of url arguments. 825 826 /some/where?arg1=don&arg2=johnson 827 828=item $self->get_auth_dbh 829 830Same as get_dbh, but for the authentication database. 831 832=item $self->get_cached_config 833 834Users should call get_config instead. 835 836Pulls the config object out of the pnotes so Gantry::Conf doesn't have 837to regenerate it repeatedly. (See set_cached_config.) 838 839=item get_config 840 841If you are using Gantry::Conf, this will return the config hash reference 842for the current location. 843 844=item get_cached_conf/set_cached_conf 845 846These cache the Gantry::Conf config hash in pnotes. Override them if 847you want more persistent caching. These are instance methods. get 848receives the invoking object, the name of the GantryConfInstance, 849and the current location (for ease of use, its also in the invocant). 850set receives those plus the conf hash it should cache. 851 852=item $self->get_dbh 853 854Returns the current regular database connection if one is available or 855undef otherwise. 856 857=item $self->get_post_body 858 859If C<consume_post_body> was used by a plugin during the pre_init phase, 860this method returns the consumed body of the HTTP POST request. 861 862=item $self->header_in 863 864The $r->headers_in method will return a %hash of client request headers. 865This can be used to initialize a perl hash, or one could use the 866$r->header_in() method (described below) to retrieve a specific header 867value directly. 868 869See mod_perl docs. 870 871=item $self->header_out( $r, $header_key, $header_value ) 872 873Change the value of a response header, or create a new one. 874 875=item $self->is_connection_secure() 876 877Return whether the current request is being served by an SSL-enabled host. 878 879=item $self->is_status_declined 880 881Returns a true value if the status is currently DECLINED or false otherwise. 882 883=item $self->log_error( message ) 884 885Writes message to the apache web server log 886 887=item $self->port 888 889Returns port number in which the request came in on. 890 891=item $self->print_output( $response_page ) 892 893This method sends the contents of $response page back to apache. It 894uses the print method on the request object. 895 896=item $self->redirect_response 897 898Returns the proper numeric status code for REDIRECT. 899 900=item $self->remote_ip 901 902Returns the IP address for the remote user 903 904=item $self->send_error_output 905 906Returns the content of custom_error. It gives $@ to the custom_error method. 907 908=item $self->send_http_header( $r ) 909 910Send the response line and all headers to the client. Takes an optional 911parameter indicating the content-type of the response, i.e. 'text/html'. 912 913This method will create headers from the $r->content_xxx() and $r->no_cache() 914attributes (described below) and then append the headers defined by 915$r->header_out (or $r->err_header_out if status indicates an error). 916 917See mod_perl 1.0 docs. 918 919=item $self->server_root 920 921Returns the value set by the top-level ServerRoot directive 922 923=item set_cached_config 924 925For internal use. Used to place a config hash into pnotes for reuse 926during the current page request. 927 928=item $self->set_content_type() 929 930Sets the content type stored in the site object's content_type attribute 931on the apache request object. 932 933=item $self->set_no_cache 934 935Sets the no_cache flag in the apache request object with the value 936for no_cache in the site object. 937 938=item set_req_params 939 940Sets up the apreq object and the form parameters from it. 941 942=item $self->status_const( 'OK | DECLINED | REDIRECT' ) 943 944Get or set the reply status for the client request. The Apache::Constants 945module provide mnemonic names for the status codes. 946 947=item $self->success_code 948 949Returns the proper numeric status code for OK. 950 951=item url_encode 952 953 url_encode($value) 954 955Accepts a value and returns it url encoded. 956 957=item url_decode 958 959 url_decode($value) 960 961Accepts a value and returns it url decoded. 962 963=item $self->file_upload 964 965Uploads a file from the client's disk. 966 967Parameter: The name of the file input element on the html form. 968 969Returns: A hash with these keys: 970 971=over 4 972 973=item unique_key 974 975a unique identifier for this upload 976 977=item name 978 979the base name of the file 980 981=item suffix 982 983the extension (file type) of the file 984 985=item fullname 986 987name.suffix 988 989=item size 990 991bytes in file 992 993=item mime 994 995mime type of file 996 997=item filehandle 998 999a handle you can read the file from 1000 1001=back 1002 1003=back 1004 1005=head1 SEE ALSO 1006 1007mod_perl(3), Gantry(3) 1008 1009=head1 LIMITATIONS 1010 1011 1012=head1 AUTHOR 1013 1014Tim Keefer <tkeefer@gmail.com> 1015 1016=head1 COPYRIGHT and LICENSE 1017 1018Copyright (c) 2005-6, Tim Keefer. 1019 1020This library is free software; you can redistribute it and/or modify 1021it under the same terms as Perl itself, either Perl version 5.8.6 or, 1022at your option, any later version of Perl 5 you may have available. 1023 1024=cut 1025