# Selima Website Content Management System # LastModf.pm: The web site last-modified time calculator. # Copyright (c) 2003-2018 imacat. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Author: imacat # First written: 2003-03-23 package Selima::LastModf; use 5.008; use strict; use warnings; use base qw(Exporter); use vars qw(@EXPORT @EXPORT_OK); BEGIN { @EXPORT = qw(not_modified); @EXPORT_OK = @EXPORT; # Prototype declaration sub not_modified(\@\@); sub find_last_modified(\@\@); sub check_inc(); sub updmtime_tables(\@); sub updmtime_file($); } use File::Spec::Functions qw(catfile); use HTTP::Date qw(str2time); use Selima::CallForm; use Selima::DataVars qw($DBH :input :l10n :lninfo :lastmod :libdir :requri :siteconf); use Selima::GetLang; use Selima::HTTP; use Selima::Session; # not_modified: Check if we should send "HTTP/1.1 304 Not Modified" sub not_modified(\@\@) { local ($_, %_); my ($tables, $files); ($tables, $files) = @_; # HTTP/1.1 304 only works for GET or HEAD return 0 unless $ENV{"REQUEST_METHOD"} eq "GET" || $ENV{"REQUEST_METHOD"} eq "HEAD"; # Find the last-modified time find_last_modified @$tables, @$files; # If-Modified-Since not supplied. The client had not cached yet. return 0 if !exists $ENV{"HTTP_IF_MODIFIED_SINCE"}; # Malformed If-Modified-Since value return 0 if !defined($_ = str2time($ENV{"HTTP_IF_MODIFIED_SINCE"})); # We are newer than the cache return 0 if $LAST_MODIFIED > $_; # Yes, use the cache return 1; } # find_last_modified: Find the last-modified time sub find_last_modified(\@\@) { local ($_, %_); my ($tables, $files); ($tables, $files) = @_; # Checked before return if defined $LAST_MODIFIED; # Remove duplicates %_ = map { $_ => 1 } @$tables; @$tables = keys %_; %_ = map { $_ => 1 } @$files; @$files = keys %_; # Start with EPOCH 1970-01-01 00:00:00 $LAST_MODIFIED = 0; # Check myself updmtime_file $0; # Check mtime from the included modules and gettext MO files check_inc; # Check the supplied data files updmtime_file $_ foreach grep -f $_, @$files; # Check the supplied data tables updmtime_tables @$tables; # Check the saved form and status if (defined $GET->param("formid")) { $_ = catfile($Selima::Session::DIR, "saveform_" . $GET->param("formid")); updmtime_file $_ if -f $_; } if (defined $GET->param("statid")) { $_ = catfile($Selima::Session::DIR, "savestat_" . $GET->param("statid")); updmtime_file $_ if -f $_; } return; } # check_inc: Check mtime from the included modules and gettext MO files sub check_inc() { local ($_, %_); my ($t, $DH); # Check the included modules updmtime_file $_ foreach grep /^\Q$SITE_LIBDIR\E\b/, values %INC; $t = COMMON_LIBDIR; updmtime_file $_ foreach grep /^$t\b/, values %INC; # Check the header and footer $_ = $DOC_ROOT . "/magicat/include"; @_ = qw(); if (@ALL_LINGUAS > 1) { push @_, $_ . "/header." . getlang(LN_FILENAME) . ".html"; push @_, $_ . "/footer." . getlang(LN_FILENAME) . ".html"; } else { push @_, $_ . "/header.html"; push @_, $_ . "/footer.html"; } foreach (@_) { updmtime_file $_ if -e $_; } # Check the gettext mo files if (-d $LOCALEDIR) { opendir $DH, $LOCALEDIR or http_500 "$LOCALEDIR: $!"; @_ = readdir $DH or http_500 "$LOCALEDIR: $!"; closedir $DH or http_500 "$LOCALEDIR: $!"; foreach (@_) { $_ = "$LOCALEDIR/$_/LC_MESSAGES/$PACKAGE.mo"; updmtime_file $_ if -f $_; } } opendir $DH, COMMON_LOCALEDIR or http_500 COMMON_LOCALEDIR . ": $!"; @_ = readdir $DH or http_500 COMMON_LOCALEDIR . ": $!"; closedir $DH or http_500 COMMON_LOCALEDIR . ": $!"; foreach (@_) { $_ = COMMON_LOCALEDIR . "/$_/LC_MESSAGES/" . COMMON_DOMAIN . ".mo"; updmtime_file $_ if -f $_; } return; } # updmtime_tables: Update the $LAST_MODIFIED with the mtime of tables sub updmtime_tables(\@) { local ($_, %_); my ($tables, $sql, $sth); $tables = $_[0]; # Only work when using database return if !defined $DBH; return unless defined($_ = $DBH->lastupd(@$tables)); $_ = int $_; $LAST_MODIFIED = $_ if defined $_ && $LAST_MODIFIED < $_; return; } # updmtime_file: Update the $LAST_MODIFIED with the mtime of a file sub updmtime_file($) { local ($_, %_); $_ = (stat $_[0])[9]; $LAST_MODIFIED = $_ if $LAST_MODIFIED < $_; return; } return 1;