=head1 NAME SuikaWiki::Output::HTTP --- SuikaWiki: HTTP or HTTP CGI output support =head1 DESCRIPTION This module provides HTTP or HTTP CGI outputing support. This module is part of SuikaWiki. =cut package SuikaWiki::Output::HTTP; use strict; our $VERSION = do{my @r=(q$Revision: 1.3 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; my %Status = ( 200 => q(OK), 201 => q(Created), 202 => q(Accepted), 203 => q(Non-Authoritative Information), 204 => q(No Content), 205 => q(Reset Content), 250 => q(Low on Storage Space), # RTSP 300 => q(Multiple Choices), 301 => q(Moved Permanently), 302 => q(Found), 303 => q(See Other), 304 => q(Not Modified), 307 => q(Moved Temporarily), 400 => q(Bad Request), 401 => q(Unauthorized), 403 => q(Forbidden), 404 => q(Not Found), 405 => q(Method Not Allowed), 406 => q(Not Acceptable), 408 => q(Request Timeout), 409 => q(Conflict), 410 => q(Gone), 413 => q(Request Entity Too Long), 414 => q(Request-URI Too Long), 415 => q(Unsupported Media Type), 423 => q(Locked), 480 => q(Temporariliy Not Available), # SIP 500 => q(Internal Server Error), 501 => q(Not Implemented), 503 => q(Service Unavailable), 505 => q(HTTP Version Not Supported), 507 => q(Insufficient Storage), ); my %Body = ( 301 => sub { my $self = shift; my $euri = $self->{redirect_uri}; $euri =~ s/&/&/g; $euri =~ s//>/g; $euri =~ s/"/"/g; qq(

See <$euri>

); }, ); $Body{302} = $Body{301}; $Body{303} = $Body{301}; $Body{307} = $Body{301}; =head1 METHODS =over 4 =item $http = SuikaWiki::Output::HTTP->new Constructs new instance of HTTP output implementation =cut sub new ($;%) { my $self = bless {header_field => [], negotiate_header_field => {Negotiate => 1}, entity => {language => [], media_type => 'application/octet-stream'}, }, shift; my %opt = @_; $self->{wiki} = ref $opt{wiki} ? $opt{wiki} : $opt{view}->{wiki}; $self->{view} = $opt{view}; $self->{viewobj} = $opt{viewobj}; $self; } sub add_header_field ($$$;%) { my ($self, $name => $body, %opt) = @_; push @{$self->{header_field}}, {name => $name, body => $body}; } sub add_negotiate_header_field ($$) { my ($self, $name) = @_; $self->{negotiate_header_field}->{$name} = 1; } sub set_expires ($%) { my ($self, %opt) = @_; if ($opt{time}) { ## TODO: use rfc1123-time push @{$self->{header_field}}, {name => 'Expires', body => scalar gmtime $opt{time}}; } elsif (defined $opt{delta}) { $self->{expires_delta} = $opt{delta} + 0; } } sub set_redirect ($%) { my ($self, %opt) = @_; $self->{redirect_uri} = $opt{uri}; if ($opt{status_code}) { $self->{status_code} = $opt{status_code}; } else { $self->{status_code} = 302; } $self->{status_phrase} = $opt{status_phrase}; } sub set_last_modified ($%) { my ($self, %opt) = @_; $self->{lastmodified_time} = $opt{time}; } sub append_to_body ($$) { my ($self, $s) = @_; $self->{entity}->{body} .= $s; } sub output ($;%) { my ($self, %opt) = @_; my $nl = $opt{output} eq 'http-cgi' ? "\n" : "\x0D\x0A"; binmode STDOUT; $| = 1; my $status_code = $self->{status_code} || 200; my $status_phrase = $self->{status_phrase} || $Status{$status_code}; if ((300 < $status_code) && ($status_code < 400) && (defined $self->{redirect_uri})) { print "Location: $self->{redirect_uri}$nl"; $status_code = 302 if ($status_code == 303 || $status_code == 307) && (($main::ENV{SERVER_PROTOCOL} eq 'HTTP/1.0') ## TODO: use common interface || ($self->{wiki}->{var}->{client}->{user_agent_name} =~ m#M(?:ozilla/[0-4]\.|icrosoft Internet Explorer)#)); } if ($opt{output} eq 'http-cgi') { print "Status: $status_code $status_phrase$nl"; } else { print "$status_code $status_phrase HTTP/1.1$nl"; } for (@{$self->{header_field}}) { print "$_->{name}: $_->{body}$nl"; } my $time = time; print "Date: @{[scalar gmtime $time]}$nl"; if (defined $self->{expires_delta}) { print "Expires: @{[scalar gmtime ($time + $self->{expires_delta})]}$nl"; } if (defined $self->{lastmodified_time}) { print "Last-Modified: @{[scalar gmtime $self->{lastmodified_time}]}$nl"; } else { print "Last-Modified: @{[scalar gmtime $time]}$nl"; } print "Vary: ".join (', ', keys %{$self->{negotiate_header_field}})."$nl"; my $mt = $self->{entity}->{media_type} || 'application/octet-stream'; my $charset = $self->{entity}->{charset}; my $body = '' . $self->{entity}->{body}; unless (length $body) { $mt = q; $charset = 'iso-8859-1'; $body = &{$Body{$self->{status_code}||200}||sub{}} ($self); } ## TODO: $body = &main::code_convert (\$body, $self->{entity}->{charset}); if (($mt eq 'application/rdf+xml') && (index ($self->{wiki}->{var}->{client}->{user_agent_name}, 'Gecko') > -1)) { print "Content-Type: application/xml"; } else { print "Content-Type: $mt"; } if ($charset) { if ($self->{wiki}->{var}->{client}->{user_agent_name} =~ m#Mozilla/[12]\.#) { print qq(; charset="@{[{qw/euc-jp x-euc-jp shift_jis x-sjis/} ->{$charset} || $charset]}"); } elsif ($self->{wiki}->{var}->{client}->{user_agent_name} =~ m#Mozilla/0\.|Infomosaic#) { } else { print qq(; charset="$charset"); } } print $nl; for (join ', ', @{$self->{entity}->{language}||[]}) { print "Content-Language: $_$nl" if $_; } print "Content-Length: @{[length $body]}$nl"; print $nl; print $body; } =item $self->exit Declares the instance ($self) no longer considered useful. =cut sub exit ($) { my $self = shift; delete $self->{wiki}; delete $self->{view}; delete $self->{viewobj}; $self->{exited} = 1; } sub DESTROY ($) { my $self = shift; $self->exit unless $self->{exited}; } =item $self->{entity}->{charset} =item $self->{entity}->{language} = [...] =item $self->{entity}->{media_type} =back =head1 TODO Use manakai. =head1 LICENSE Copyright 2003 Wakaba This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # $Date: 2003/12/06 05:38:13 $