diff -urN ../htmllint.orig/tagslist.cgi ./tagslist.cgi --- ../htmllint.orig/tagslist.cgi Sat Sep 8 01:00:00 2012 +++ ./tagslist.cgi Thu Sep 13 13:00:00 2012 @@ -151,13 +151,11 @@ $CHARSET = 'ISO-2022-JP'; } } -if ($TAGSLIST) { - # 規則ファイルの日付を調べる - $RULETIME = (stat($0))[9]; - foreach (keys %doctypes) { - my $t = (stat($RULEDIR.$_.'.rul'))[9]; - $RULETIME = $t if $RULETIME < $t; - } +# 規則ファイルの日付を調べる +$RULETIME = (stat($0))[9]; +foreach (keys %doctypes, 'common') { + my $t = (stat($RULEDIR.$_.'.rul'))[9]; + $RULETIME = $t if $RULETIME < $t; } $rule = 'all' unless $rule; $| = 1; @@ -166,7 +164,18 @@ '|NUMBERS|NAMES|NMTOKENS|NUTOKENS|ID|IDREF|ENTITY'; $internalElem = '#\d+'; -if ($tag) { +if (!$nocgi && + defined($ifModifiedSince = &str2time($ENV{'HTTP_IF_MODIFIED_SINCE'})) && + $ifModifiedSince >= $RULETIME) { + # HTTPの要件(rfc2616 10.3.5)では、この場合はContent-Typeなどの + # エンティティヘッダを出力すべきではないとされているが、 + # CGIの要件(rfc3875 6.2.1)では、最低でもContent-Typeは出力する必要がある。 + print qq|Status: 304 Not Modified\x0D\x0A|, + qq|Content-Type: text/html; charset=$CHARSET\x0D\x0A\x0D\x0A|; +} elsif (!$nocgi && $ENV{'REQUEST_METHOD'} eq 'HEAD') { + # メッセージボディを返さないことを除いてGETと同じ(rfc2616 9.4) + &PrintHTTPHeader; +} elsif ($tag) { $tag = "\U$tag"; &SelectTAGSLIST($tag); &PrintHTTPHeader unless $TAGSLIST; @@ -561,7 +570,11 @@ sub PrintHTTPHeader { - print qq|Content-Type: text/html; charset=$CHARSET\x0D\x0A\x0D\x0A| unless $nocgi; + unless ($nocgi) { + my $modtime = &time2str($RULETIME); + print qq|Last-Modified: $modtime\x0D\x0A|, + qq|Content-Type: text/html; charset=$CHARSET\x0D\x0A\x0D\x0A|; + } } sub PrintHTMLHeader { @@ -606,6 +619,40 @@ } ############################################################## +# HTTP::Date互換日付変換ルーチン + +sub time2str +{ + my $time = shift; +# $time = time unless defined($time); + sprintf('%s, %02d %s %d %s GMT', @{[split(/\s+/, gmtime($time))]}[0,2,1,4,3]); +} + +sub str2time +{ + my $str = shift; + return undef unless defined($str); + my $MoY = 'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec'; + my @tm; + + # HTTP::Dateとは異なり、後ろの余計な文字は無視する + if ($str =~ /^\s*[SMTWF][a-z]{2,}, +(\d\d?) +($MoY) +(\d{4}) +(\d\d):(\d\d):(\d\d) +GMT/) { + @tm = ($6, $5, $4, $1, index($MoY,$2)/4, $3); # rfc1123 HTTP format + } elsif ($str =~ /^\s*[SMTWF][a-z]{2,}, +(\d\d?)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT/) { + @tm = ($6, $5, $4, $1, index($MoY,$2)/4, $3); # rfc850 HTTP format + } elsif ($str =~ /^\s*[SMTWF][a-z]{2,} +($MoY) +(\d\d?) +(\d\d):(\d\d):(\d\d) +(\d{4})/) { + @tm = ($5, $4, $3, $2, index($MoY,$1)/4, $6); # ANSI C asctime() format + } else { + return undef; + } + return eval { + require Time::Local; + my $t = Time::Local::timegm(@tm); + $t < 0 ? undef : $t; + }; +} + +############################################################## sub SelectTAGSLIST {