#!/usr/local/bin/perl
##############################################################################
# 掲示版CGI
# ========= 
#
# フォームからの引数
# ==================
#	name	名前
#	email	E-Mailアドレス
#	url	URL
#	comment	書き込み
#
#	dir	記事保存用のディレクトリ（隠し引数）
#
# 掲示版の各ファイルの位置（想定）
# ===============================
#       $dir フォームからの引数を使用
#
#	$dir/index.shtml ... 掲示版入力画面
#                       /new.txt     ... 最新の記事リスト(index.shtmlに挿入)
#                       /list1.shtml ... 過去の記事リスト
#			/list1.txt   ...   list1.shtml に挿入
#                       /list2.shtml ... 過去の記事リスト
#			/list2.txt   ...   list2.shtml に挿入
#			 :
#
#		index.shtml -> list2.html -> list1.html
#		新しい記事リスト	     古い記事リスト
#
#
# 設定方法
# ========
#      1) cgilib.pl, jcode.pl, fileutil.pl, tmputil.pl が置いている
#         ディレクトリをサーチパスに設定
#            unshift(@INC, '/www/htdocs/fuji/_HI-HO/BBS/common');
#
#      2) 次の変数を環境に合わせて設定する(各変数の説明は後述)
#	     $dir_flg
#            $dir_public_html
#            $dir_lelative
#            $templete_dir
#
#      3) 記事を保存するディレクトリにWrite権をつける
#            chmod o+w or chmod g+w
#
##############################################################################
unshift(@INC, 'board_lib');

require 'cgilib.pl';
require 'jcode.pl';
require 'fileutil.pl';
require 'tmputil.pl';

#=====================================================================
# 記事を格納しているディレクトリを得る方法
# 
#      記事を格納するディレクトリ        board(FORMからの引数)
#
#      ＣＧＩのディレクトリ              /home/fuji/public_html/cgi-bin
#      記事を格納するディレクトリ        /home/fuji/public_html/board
#
# 1)フォーム引数から得る場合
#      各変数を以下の様に設定する
#      ユーザホームディレクトリはユーザアカウントから得る
#      $dir_flg         = 0;
#
#      	$dir_lelative は未使用
#
# 2)ＣＧＩからの相対パスから得る場合
#      各変数を以下の様に設定する
#      $dir_flg         = 1;
#      $dir_lelative    = '..'
#
#=====================================================================
$dir_flg         = 0;             # 0:フォーム引数から得る場合
                                  # 1:相対パスからディレクトリを得る

$dir_lelative    = '..';          # 掲示板ＣＧＩからユーザホームページ
                                  # へのディレクトリ

#=====================================================================
# 記事テンプレートを置いているディレクトリ
#(テンプレートファイルはEUCコードでセーブすること)
#
# $template_kiji ... new.txt の１記事のテンプレート
# $template_list ... list1.shtml, list2.shtml..のテンプレート
# $template_link ... oldidx.txt の１リンクのテンプレート
#=====================================================================
$templete_dir   = 'board_lib';

$template_kiji  = $templete_dir.'/kiji.tmp'; # １記事のテンプレート
$template_list 	= $templete_dir.'/list.tmp'; # 過去の記事リストテンプレート
$template_link 	= $templete_dir.'/link.tmp'; # 過去の記事リストへのリンクテンプレート

#=====================================================================
# ＝記事を追加する場合＝
#	1) new.lock のオープン＋LOCK
#	2) new1.txt の Writeオープン+最新記事書き込み
#	3) new.txt の Readオープン
#	4) new.txt を Read -> new1.txt に Write
#	5) new.txt, new1.txt のクローズ
#	6) new1.txt を new.txt にリネーム
#	7) new.lock を UNLOCK＋クローズ
#
# ＝記事を別ファイルに保存する場合＝
#	1) new.lock のオープン＋LOCK
#	2) new.txt を過去の記事へリネーム
#	3) new.txt の Writeオープン+最新記事書き込み
#	4) new.txt のクローズ
#	5) new.lock を UNLOCK＋クローズ
#
# 過去の記事は 
# new.txt を list1.txt, list2.txt, list3.txt ... とファイル名を変更し、
# 各ファイルを list1.shtml, list2.shtml, list3.shtml ...に差し込む
# list1.shtml 等へのリンクは oldidx.txt に追加する。
#
# ファイルイメージ
# ----------------
# index.shtml
# ┏━━━━━┓
# ┃┌────┸┐
# ┃│new.txt   │ ...最新記事リスト
# ┃└────┰┘
# ┃┌────┸┐
# ┃│oldidx.txt│ ...過去の記事リストへのリンク
# ┃│<A HREF...├┐
# ┃└────┰┘│
# ┗━━━━━┛  ├─→ list1.shtml
#                 │    ┏━━━━━┓
#                 │    ┃┌────┸┐
#                 │    ┃│list1.txt │ ...過去の記事リスト１
#                 │    ┃└────┰┘
#                 │    ┗━━━━━┛
#                 │
#                 ├─→ list2.shtml
#                 │    ┏━━━━━┓
#                 │    ┃┌────┸┐
#                 │    ┃│list2.txt │ ...過去の記事リスト2
#                 │    ┃└────┰┘
#                 │    ┗━━━━━┛
#                 ：
# 
#=====================================================================
$kiji_lock	= 'new.lock';	# 排他制御用ファイル
$kiji_list	= 'new.txt';	# 最新の記事リスト
$kiji_list1	= 'new1.txt';	# データ更新用作業ファイル
$kiji_oldidx	= 'oldidx.txt'; # 過去の記事HTMLインデックス差し込みファイル
$kiji_num	= 50;		# １ファイル保存記事数

#----------------------------------------------
# フォームからの引数名（エラーメッセージに使用）
#----------------------------------------------
%field_name = (
	'name',		'お名前',
	'email',	'E-Mail',
	'url',		'URL',
	'comment',	'書き込み',
	'dir',		'記事保存先',
);

#----------------------------------
# フォームからの引数チェックフラグ
#	1:省略不可、0:省略可
#----------------------------------
%field_check = (
	'name',		1,
	'email',	0,
	'url',		0,
	'comment',	1,
	'dir',		1,
);

$Jcode = 'x-sjis'; # デフォルト

#
# フォームからの引数を得る
#
&cgilib'ReadParse(*field);
&CheckField(*field, *field_name, *field_check);

if ($dir_flg == 0) {
#	$kiji_dir = (getpwnam($field{'user'}))[7].'/'.$dir_public_html.'/'.$field{'dir'};
	$kiji_dir = $ENV{'HOME'}.'/html/'.$field{'dir'};
}
else {
	$kiji_dir = $dir_lelative.'/'.$field{'dir'};
}

unless ( -d $kiji_dir && -w $kiji_dir) {
	&cgilib'DieHtml("記事格納ディレクトリ $kiji_dir に記事が書き込めません", $Jcode);
}

#
# 記事を生成（テンプレートを使用）
#
&InitInsertData(*field, *id);
$status = &tmputil'InsertFile(*id, $template_kiji, *data);
if ($status == 0) {
	&cgilib'DieHtml("記事の生成に失敗しました。", $Jcode);
}

#
# 記事リストに追加する
#
unless (&AddList($kiji_dir, $kiji_list, $kiji_num, $data)) {
	&cgilib'DieHtml("記事リスト生成に失敗しました。", $Jcode);
}

&cgilib'DieHtml($data."記事を受けとりました。", $Jcode);
exit 0;


##############################################################################
# CheckField		引数チェック
#
#	*field	[I]フォームから受けとった引数
##############################################################################
sub CheckField {
	local(*field, *field_name, *field_check) = @_;
	local($key, $name, $val);
	local($list, $message, $lhead, $lfoot);

	$list = '';
	while(($key, $name) =each %field_name) {
		$val = $field{$key};
		if (length($val) == 0 && $field_check{$key} == 1) {
			$list .= '<LI>'.$name;
		}
	}
	if ($list ne '') {
		$message = '以下の項目を入力して下さい。<BR>';
		$lhead = '<UL>';
		$lfoot = '</UL>';

		&cgilib'DieHtml($message.$lhead.$list.$lfoot, $Jcode);
	}
}

##############################################################################
# InitInsertData	テンプレートに埋め込む文字列の作成
#
#	*filed	[I]フォームから受けとった引数
#	*id	[O]テンプレートに埋め込む文字列
#
##############################################################################
sub InitInsertData {
	local(*field, *id) = @_;
	local($tmp1, $tmp2);

	$tmp2 = &GetDate;
	&EntryRelation(*id, '%%date%%', $tmp2, 1, '');
	&EntryRelation(*id, '%%name%%', $field{'name'}, 1, '');

	$tmp2 =  $field{'comment'};
	$tmp2 =~ s/\n/<BR>\n/g;
	&EntryRelation(*id, '%%comment%%', $tmp2, 1, '');

	#
	# EMailは記述している場合のみ
	#
	if (length($field{'email'}) != 0) {
		$tmp1 = $field{'email'};
		$tmp2 = "(<A HREF=\"mailto:$tmp1\">$tmp1</A>)";
	}
	else {
		$tmp2 = '';
	}
	&EntryRelation(*id, '%%email%%', $tmp2, 1, '');

	#
	# URLは記述している場合のみ
	#
	if (length($field{'url'}) != 0) {
		$tmp1 = $field{'url'};
		$tmp2 = "URL:<A HREF=\"$tmp1\">$tmp1</A>";
	}
	else {
		$tmp2 = '';
	}
	&EntryRelation(*id, '%%url%%', $tmp2, 1, '');
}

###########################################################################
# EntryRelation		連想配列に値を格納
#
#	*Arr		[O]連想配列名
#	$Key		[I]連想配列キー
#	$Val		[I]連想配列値
#	$Inst		[I]キーが既にある場合の動作
#			   0:格納しない, 
#			   1:上書きする, 
#			   2:指定のセパレータを挟んで追加する。
#	$Sep		[I]セパレータ($Inst=2のときのみ使用)
###########################################################################
sub EntryRelation {
	local(*Arr, $Key, $Val, $Inst, $Sep) = @_;

#	&jcode'convert(*Key, 'euc');
#	&jcode'convert(*Val, 'euc');
	# キーが既に存在する場合
	if ( defined $Arr{$Key} ) {
		# 格納しない（先頭優先）
		if ($Inst == 0) {
		}
		# 上書き（後方優先）

		elsif($Inst == 1) {
			$Arr{$Key} = $Val;
		}
		# セパレータを挟んで追加
		elsif($Inst == 2) {
			$Arr{$Key} .= $Sep.$Val;
		}
	}
	# 初回追加
	else {
		unshift(@Arr, $Key);
		$Arr{$Key} = $Val;
	}
}

###########################################################################
# GetDate		今日の日付を得る
###########################################################################
sub GetDate {
	local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
	local($month);
	local($datestr);

	#サーバーから日付／時間を得る
	($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
	$month = $mon + 1;

	$datestr = sprintf("%02d年 %02d月 %02d日 %02d時 %02d分 %02d秒", $year, $month, $mday, $hour, $min, $sec);
}

###########################################################################
# AddList		記事を記事リストに追加する
###########################################################################
sub  AddList {
	local($kiji_dir, $kiji_list, $kiji_num, $data) = @_;
	local($wlock, $wkiji, $wkiji1);
	local(*LOCK, *NEW, *NEW1, *LIST);
	local($status);
#	umask (011);

	$wlock = $kiji_dir.'/'.$kiji_lock;
	$wkiji = $kiji_dir.'/'.$kiji_list;
	$wkiji1 = $kiji_dir.'/'.$kiji_list1;

	# 他のプロセスが割り込まないようにロックする。
	unless( &fileutil'openfilel(*LOCK, $wlock, 'w') ) {
		return 0;
	}

	#
	# 最新記事リストが指定記事数を得る
	#
	$no = &GetKijiNum($wkiji);
	if ($no == -1) {
		&fileutil'closefilel(*LOCK);
		return 0;
	}
	elsif ($no >= $kiji_num) {
		#------------------------------------------------------ 
		# 最新記事リストの記事数が指定記事数を越えていれば
		# 別ファイルに保存 
		#------------------------------------------------------ 
		unless (&ChangeKijiList($wkiji, $kiji_dir, $date)) {
			&fileutil'closefilel(*LOCK);
			return 0;
		}

		#------------------------------------------------------ 
		# 最新記事の新規作成
		#------------------------------------------------------ 

		# 作業用最新記事リストをオープンする
		unless( &fileutil'openfilel(*NEW1, $wkiji, 'w') ) {
			&fileutil'closefilel(*LOCK);
			return 0;
		}
		$no = 1;
		&PrintJ(*NEW1, "<!-- $no -->\n");
		&PrintJ(*NEW1, $data);
		&fileutil'closefilel(*NEW1);
	}
	else {

		#------------------------------------------------------ 
		# 最新記事の追加、最新の記事を最上位置に持ってくるため
		# 作業ファイルを作成、最新記事、既存記事の順に書き込む
		# 作業ファイルから、記事リストファイルにリネーム
		#------------------------------------------------------ 

		# 作業用最新記事リストをオープンする
		unless( &fileutil'openfilel(*NEW1, $wkiji1, 'w') ) {
			&fileutil'closefilel(*LOCK);
			return 0;
		}
		# 最新記事の書き込み
		$no++;
		&PrintJ(*NEW1, "<!-- $no -->\n");
		&PrintJ(*NEW1, $data);
	
		# 最新記事リストをオープンする
		unless( &fileutil'openfilel(*NEW, $wkiji, 'r') ) {
			&fileutil'closefilel(*LOCK);
			&fileutil'closefilel(*NEW1);
			return 0;
		}
		# 最新記事リストの内容を作業ファイルへ
		while(<NEW>) {
			&PrintJ(*NEW1, $_);
		}
		&fileutil'closefilel(*NEW);
		&fileutil'closefilel(*NEW1);
		rename($wkiji1, $wkiji);
	}

	# ロックの解除
	&fileutil'closefilel(*LOCK);	
}

###########################################################################
# 記事リスト内の記事数を得る
###########################################################################
sub GetKijiNum {
	local($list) = @_;
	local(*NEW);
	local($no);

	$no = 0;

	# 最新記事リストをオープンする
	unless( &fileutil'openfilel(*NEW, $list, 'r') ) {
		return(-1);
	}

	while(<NEW>) {

		if ($_ =~ /<!--\s*(\d+)\s*-->/) {
			$no = $1;
			last;
		}
	}

	&fileutil'closefilel(*NEW);
	$no;
}

###########################################################################
# 最新記事リストを過去の記事リストに移し買える。
###########################################################################
sub ChangeKijiList {
	local($kiji_txt, $kiji_dir, $date) = @_;  
	local($listno);
	local($old_shtml, $old_txt);
	local($old_shtml_path, $old_txt_path, $old_idx_path);
	local($data, %ins_list1, %ins_list2);
	local(*OLD, *IDX);

	#
	# 過去の記事HTMLファイル名の最大番号を得る。
	#
	$listno = &GetLastNo_List($kiji_dir);
	$listno++;

	
	$old_shtml = 'list'.$listno.'.shtml';
	$old_txt   = 'list'.$listno.'.txt';

	$old_shtml_path = $kiji_dir.'/'.$old_shtml;
	$old_txt_path   = $kiji_dir.'/'.$old_txt;
	$old_idx_path   = $kiji_dir.'/'.$kiji_oldidx;

	# 最新記事リストtxtファイルを過去の記事リストへリネーム
	rename($kiji_txt, $old_txt_path);

	#
	# 過去の記事リストshtmlファイルの生成
	#
	$ins_list1{'%%code%%'} = $Jcode;
	$ins_list1{'%%list%%'} = $old_txt;
	unless( &tmputil'InsertFile(*ins_list1, $template_list, *data)) {
		return 0;
        }

	unless( &fileutil'openfilel(*OLD, $old_shtml_path, 'w') ) {
		return 0;
	}
	&PrintJ(*OLD, $data);
	&fileutil'closefile(*OLD);

	#
	# 過去の記事リストのインデックス生成
	#
	$ins_list2{'%%shtml%%'} = $old_shtml;
	$ins_list2{'%%linkname%%'} = "過去の記事リスト$listno";
	unless( &tmputil'InsertFile(*ins_list2, $template_link, *data)) {
		return 0;
        }

	unless( &fileutil'openfilel(*IDX, $old_idx_path, 'a') ) {
		return 0;
	}
	&PrintJ(*IDX, $data);
	&fileutil'closefile(*IDX);
	1;
}

###########################################################################
# 指定ディレクトリ内の list%d.html の最大数を得る
###########################################################################
sub GetLastNo_List {
	local($kiji_dir) = @_;
	local(*FD);
	local(@alllists, $l, $maxno, $no);

	opendir(FD, $kiji_dir);
	@alllists = grep (/^list\d+.shtml$|^list\d+.txt$/, readdir(FD));
	closedir(FD);

	$maxno = 0;
	foreach $l (@alllists) {
		if ( $l =~ /list(\d+).shtml/) {
			$no = $1;
	    	}
		elsif ($l =~ /list(\d+).txt/) {
			$no = $1;
		}

		if ($no > $maxno) {
			$maxno = $no;
		}
	}
#&cgilib'DieHtml("@alllists ... $maxno", $Jcode);
	$maxno;
}


##############################################################################
# 漢字コード変換機能つき文字列表示
##############################################################################
sub PrintJ {
        local(*FD, $str) = @_;
        local($tmpstr);

        $tmpstr = $str;

        if ($Jcode eq 'x-sjis') {
                &jcode'euc2sjis(*tmpstr);
        }
        elsif ($Jcode eq 'iso-2002-jp') {
                &jcode'euc2jis(*tmpstr);
        }
        print FD $tmpstr;
}

