#!/usr/bin/perl
# 日付を調べてメッセージを出力
# date_msg.cgi?西暦[0-]&月[1-12]&日[1-31]&期間[1-]&文字コード[sjis|euc]&改行するかどうか[0=no|1=yes]&HTML[html|xhtml]&表示するメッセージ
# 指定した年月日とサーバのローカルタイムを比較し,
# 等しければメッセージを表示する.
# またその年月日 + 期間の間も表示される.
# その日だけ表示したいなら期間は1にする.
# 年や,月に0が指定されたときは年や月を評価しない.
# すなわち年に0が指定された場合,全ての年が該当することになる.
# 期間の判定には西暦0年から何日かを調べて比較している.
# e.g.(1) date_msg.cgi?2005&1&1&1&sjis&1&html&ほげほげ
# ならばサーバの時間が2005年1月1日のときだけ Shift-JIS でほげほげ
と出力される.
# e.g.(2) date_msg.cgi?0&12&25&7&euc&1&xhtml&ふがふが
# ならばサーバの時間が12月25日〜12月31日の間だけ EUC-JP でふがふが
と出力される.
# jcode.pl を使う
require 'jcode.pl';
&main;
# メイン関数
sub main {
# 引数取得
$buf = $ENV{'QUERY_STRING'};
# 引数分解
($year, $month, $day, $term, $charset, $br, $html, $msg) = split (/&/, $buf);
# ヘッダ出力
&print_http_header($charset);
# 年が数字であるかチェック
# 年が0〜かチェック
&check_year ($year);
# 月が数字であるかチェック
# 月が1〜12かチェック
&check_month ($month);
# 日が数字であるかチェック
# 日が1〜31かチェック
&check_day ($day);
# ローカルの年月日を取得
&get_localtime (\$local_sec, \$local_min, \$local_hour, \$local_mday, \$local_mon, \$local_year, \$local_wday, \$local_yday, \$local_isdst);
# 年が0のときに総日数が正しく計算できるようにするための処理
if ($year == 0) {
$year = $local_year;
$year_flag = 1;
}
# 月が0のときに総日数が正しく計算できるようにするための処理
if ($month == 0) {
$month = $local_mon;
$month_flag = 1;
}
# 西暦0年から何日目か調べる
&get_yday ($year, $month, $day, \$yday, $local_year, \$local_yday);
# 年を0に戻す
if ($year_flag == 1) {
$year = 0;
}
# 月を0に戻す
if ($month_flag == 1) {
$month = 0;
}
# 期間が数字であるかチェック
# 数字でなければ0にする
&check_term (\$term);
# メッセージのチェック
# 文字のアンエスケープ,タグ除去処理
&process_msg (\$msg);
# 文字コードの変換
&convert_charset ($charset, \$msg);
# メッセージの表示
&output_msg ($year, $month, $yday, $term, $local_year, $local_mon, $local_yday, $msg, $html);
}
# HTTP ヘッダ出力
sub print_http_header {
print "Content-type: text/html; ";
# EUC が指定されたとき
if ($charset eq euc) {
print "charset=euc-jp\n\n";
}
# Shift-JIS が指定されたとき
elsif ($charset eq sjis) {
print "charset=shift_jis\n\n";
}
# デフォルトは Shift-JIS
else {
print "charset=shift_jis\n\n";
}
}
# 年をチェック
sub check_year {
my ($year) = @_;
# 数字ではないか,0より小さいとき
if (($year !~ /^\d+$/) || ($year < 0)) {
print "Year is invalid.";
exit (-1);
}
}
# 月をチェック
sub check_month {
my ($month) = @_;
# 数字ではない or 0より小さい or 12より大きい
if (($month !~ /^\d+$/) || ($month < 0) || ($month > 12)) {
print "Month is invalid.";
exit(-1);
}
}
# 日をチェック
sub check_day {
my ($day) = @_;
# 数字ではない or 1より小さい or 31より大きい
if (($day !~ /^\d+$/) || ($day < 1) || ($day > 31)) {
print "Day is invalid.";
exit(-1);
}
}
# ローカルの時間を取得
sub get_localtime {
my ($local_sec, $local_min, $local_hour, $local_mday, $local_mon, $local_year, $local_wday, $local_yday, $local_isdst) = @_;
($$local_sec, $$local_min, $$local_hour, $$local_mday, $$local_mon, $$local_year, $$local_wday, $$local_yday, $$local_isdst) = localtime (time);
# 年,月を補正する
$$local_year += 1900;
$$local_mon++;
# $$local_yday に 1 を足す(当日も経過した日として加算する)
$$local_yday++;
}
# 西暦0年からの日数を取得
sub get_yday {
my ($year, $month, $day, $yday, $local_year, $local_yday) = @_;
my ($i);
# $$yday を初期化
$$yday = 0;
# $$yday に西暦0年からの年数分の日を加算
for ($i = 1; $i < $year; $i++) {
# 閏年を考慮
if ((($i % 4) == 0) && ((($i % 100) != 0) || (($i % 400) == 0))) {
$$yday += 366;
}
else {
$$yday += 365;
}
}
# $$yday に月数分の日を加算
for ($i = 1; $i < $month; $i++) {
# 1,3,5,7,8,10,12は31日
if (($i == 1) || ($i == 3) || ($i == 5) || ($i == 7) || ($i == 8) || ($i == 10) || ($i ==12)) {
$$yday += 31;
}
# 4,6,9.11は30日
elsif (($i == 4) || ($i == 6) || ($i == 9) || ($i == 11)) {
$$yday += 30;
}
# 2月は閏年チェック
elsif ($i == 2) {
if ((($year % 4) == 0) && ((($year % 100) != 0) || (($year % 400) == 0))) {
$$yday += 29;
}
else {
$$yday += 28;
}
}
}
# $$yday に日を加算
$$yday += $day;
# $$local_yday は既にその年の分の日は加算されているので
# 西暦0年からの日数を加算するだけでよい
for ($i = 1; $i < $local_year; $i++) {
if ((($i % 4) == 0) && ((($i % 100) != 0) || (($i % 400) == 0))) {
$$local_yday += 366;
}
else {
$$local_yday += 365;
}
}
}
# 期間をチェック
sub check_term {
my ($term) = @_;
# 数字ではない or 1より小さいとき,期間 $$term は1とする
if (($$term !~ /^\d+$/) || ($$term < 1)) {
$$term = 1;
}
}
# メッセージの処理
sub process_msg {
my ($msg) = @_;
my ($text_regrex);
my ($msg_output);
my ($tag_tmp);
my ($text_tmp);
# 文字をアンエスケープする
$$msg =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
# ダグ除去処理のために EUC-JP に変換
&jcode::convert (\$$msg, 'euc');
# タグ除去処理
$text_regex = q{[^<]*};
$msg_output = '';
while ($$msg =~ /($text_regex)($tag_regex)?/gso) {
last if $1 eq '' and $2 eq '';
$msg_output .= $1;
$tag_tmp = $2;
if ($tag_tmp =~ /^<(XMP|PLAINTEXT|SCRIPT)(?![0-9A-Za-z])/i) {
$$msg =~ /(.*?)(?:<\/$1(?![0-9A-Za-z])$tag_regex_|$)/gsi;
($text_tmp = $1) =~ s/</g;
$text_tmp =~ s/>/>/g;
$msg_output .= $text_tmp;
}
}
$$msg = $msg_output;
}
# 呼び出し側 HTML の文字コードによって変える
sub convert_charset {
my ($charset, $msg) = @_;
# $msg を EUC-JP に変換する
if ($charset eq euc) {
&jcode::convert(\$$msg, 'euc');
}
# $msg を Shift-JIS に変換する
elsif ($charset eq sjis) {
&jcode::convert (\$$msg, 'sjis');
}
# デフォルトは Shift-JIS
else {
&jcode::convert (\$$msg, 'sjis');
}
}
# メッセージの出力
sub output_msg {
my ($year, $month, $yday, $term, $local_year, $local_mon, $local_yday, $msg, $html) = @_;
# 年が一致するか or 年が0
if (($year == $local_year) || ($year == 0)) {
#月が一致するか or 月が0
if (($month == $local_mon) || ($month == 0)) {
# 日が期間内に入っているかどうか
if (($local_yday >= $yday) && ($local_yday < ($yday + $term))) {
print "$msg";
# 改行するかどうか
if ($br == 1) {
&print_br($html);
}
}
}
}
}
# HTML の改行を出力
sub print_br {
my ($html) = @_;
# HTML は
if ($html eq html) {
print "
";
}
# XHTML は
elsif ($html eq xhtml) {
print "
";
}
# デフォルトは
else {
print "
";
}
}