#!/usr/bin/perl

#┌─────────────────────────────────
#│ E-PAD v2.43 (2007/01/15)
#│ Copyright (c) KentWeb
#│ webmaster@kent-web.com
#│ http://www.kent-web.com/
#└─────────────────────────────────
$ver = 'E-PAD v2.43';
#┌─────────────────────────────────
#│ [注意事項]
#│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した
#│    いかなる損害に対して作者は一切の責任を負いません。
#│ 2. 設置に関する質問はサポート掲示板にお願いいたします。
#│    直接メールによる質問は一切お受けいたしておりません。
#└─────────────────────────────────
#
# 【ファイル構成例】
#
#  public_html (ホームディレクトリ)
#      |
#      +-- epad / epad.cgi [705]
#            |    elog.cgi [606]
#            |    num.dat  [606]
#            |
#            +-- lib / jcode.pl [604]
#            |         admin.pl [604]
#            |         ptel.pl  [604]
#            |         pcom.pl  [604]
#            |
#            +-- data / i2e.dat [604]
#            |          i2j.dat [604]
#            |          j2e.dat [604]
#            |
#            +-- img / *.gif
#            |
#            +-- i / *.gif  *.png
#            |
#            +-- j / *.gif
#            |
#            +-- e / *.gif  *.png
#            |
#            +-- lock [707] /
#

#-------------------------------------------------
#  ▽基本設定
#-------------------------------------------------

# 外部ファイル取り込み
$jcode = './lib/jcode.pl';
$ptel  = './lib/ptel.pl';
$pcom  = './lib/pcom.pl';
$admin = './lib/admin.pl';

# 掲示板タイプ
#  1 : 標準タイプ（レス機能なし）
#  2 : 返信レス式（レス記事はクリック後に表示）
#  3 : 返信レス式（レス記事は初期画面から表示）
$bbstype = 3;

# 自動ソート
#  → 返信時に親記事をトップへ自動ソートする
#  0 : しない
#  1 : する
$topsort = 1;

# タイトル名
$title = "掲示板";

# タイトル文字の色
$t_color = "#666666";

# タイトル文字サイズ
$t_size = '24px';

# 本文文字サイズ
$b_size = '13px';

# 本文文字のタイプ
$b_face = "MS UI Gothic, ＭＳ Ｐゴシック, Osaka";

# 記事題名の色
$sub_col = "#DD0000";

# 記事下地の色 (PCモード時)
$tbl_col = "#ffffff";

# 記事番号の色 (PCモード時)
$num_col = "#008000";

# CGI本体スクリプト (URL)
$script = './epad.cgi';

# ログファイル (サーバパス)
$logfile = './elog.cgi';

# データ番号ファイル (サーバパス)
$numfile = './num.dat';

# 管理用パスワード
$pass = 'loveice';

# 最大記事数
#  → 親記事単位
#  → これを超える記事は古い順に削除
$max = 100;

# 戻り先 (URL)
$home = "../index.html";

# bodyタグ (PCモードの場合）
$body = '<body bgcolor="#F0F0F0" text="#000000" link="#0000FF" vlink="#800080" onload="window.focus()">';

# URLの自動リンク (0=no 1=yes)
$autolink = 1;

# 1ページ当り記事表示件数
$pageLog  = 10;  # PCモード
$pageLog2 = 5;   # 携帯モード

# ファイルロック形式
#  → 0=no 1=symlink関数 2=mkdir関数
$lockkey = 0;

# ロックファイル名
$lockfile = './lock/epad.lock';

# メール通知機能
#   0 : 通知しない
#   1 : 通知する → 自分の投稿記事も通知する
#   2 : 通知する → 自分の投稿記事は通知しない
$mailing = 0;

# メールソフトのパス（メール通知する場合）
$sendmail = '/usr/lib/sendmail';

# メール通知先アドレス（メール通知する場合）
$mailto = 'xxx@xxx.xxx';

# ホスト取得方法
# 0 : gethostbyaddr関数を使わない
# 1 : gethostbyaddr関数を使う
$gethostbyaddr = 0;

# アクセス制限（半角スペースで区切る、アスタリスク可）
#  → 拒否ホスト名を記述（後方一致）【例】*.anonymizer.com
$deny_host = '';
#  → 拒否IPアドレスを記述（前方一致）【例】210.12.345.*
$deny_addr = '';

# １回当りの最大投稿サイズ (bytes)
$maxData = 51200;

# 同一IPアドレスからの連続投稿時間（秒数）
#  → 連続投稿などの荒らし対策
#  → 値を 0 にするとこの機能は無効
$wait = 60;

# 禁止ワード
# → 投稿時禁止するワードをコンマで区切る
$no_wd = '';

# 日本語チェック（投稿時日本語が含まれていなければ拒否する）
# 0=No  1=Yes
$jp_wd = 1;

# URL個数チェック
# → 投稿コメント中に含まれるURL個数の最大値
$urlnum = 3;

# 一般画像ディレクトリURL
$imgurl = 'http://contest2007.thinkquest.jp/tqj2007/90087/img';

# i-mode画像ディレクトリURL
$img_i = './i';

# vodafone画像ディレクトリURL
$img_j = './j';

# EZweb画像ディレクトリURL
$img_e = './e';

# コメント部投稿データ制限（バイト）
$maxdata = 1000;

# ヘッダのContent-Lengthを表示する
# → 0=no 1=yes
# → 無料HPサービス等で広告バナーが自動表示される場合にはNOとする
$cont_len = 1;

# 絵文字変換データディレクトリ
$emodir = './data';

#-------------------------------------------------
#  △設定完了
#-------------------------------------------------

&agent;
&decode;
&axscheck;
if ($mode eq 'regist') { &regist; }
elsif ($mode eq 'find') { &find; }
elsif ($mode eq 'howto') { &howto; }
elsif ($mode eq 'admin') { require $admin; &admin; }
elsif ($mode eq 'usrdel') { &usrdel; }
elsif ($mode eq 'popup') { &popup; }
elsif ($mode eq 'check') { &check; }
&html;

#-------------------------------------------------
#  機種チェック
#-------------------------------------------------
sub agent {
	# 機種情報
	local($agent) = $ENV{'HTTP_USER_AGENT'};

	# jcode.pl使用フラグ
	$jflag=0;

	# i-mode
	if ($agent =~ /DoCoMo/i) {
		$type = 'i';
		$method = 'post';
		$button = 'accesskey';
		$emo1 = '&#63879;';
		$emo2 = '&#63880;';
		$emo3 = '&#63881;';
		$emo4 = '&#63882;';
		$emo5 = '&#63883;';
		$meta = '<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">';

		require $ptel;

	# vodafone
	} elsif ($agent =~ m|J-PHONE/[^\/]+/([^\/]+)|i || $agent =~ /Vodafone/|i || $agent =~ /^MOT\-/|i) {
		$type = 'j';
#		$model = $1; # 機種
#		if ($model =~ /51/) { $model=51; } else { $model=0; }
		$method = 'get';
		$button = 'DIRECTKEY';
		$emo1 = pack('H2H2','1B','24') . 'F' . pack('H2','3c') . pack('H2','0F');
		$emo2 = pack('H2H2','1B','24') . 'F' . pack('H2','3d') . pack('H2','0F');
		$emo3 = pack('H2H2','1B','24') . 'F' . pack('H2','3e') . pack('H2','0F');
		$emo4 = pack('H2H2','1B','24') . 'F' . pack('H2','3f') . pack('H2','0F');
		$emo5 = pack('H2H2','1B','24') . 'F' . pack('H2','40') . pack('H2','0F');
		$meta = '<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">';

		require $ptel;

	# EZweb
	} elsif ($agent =~ /UP\.Browser/i) {
		$type = 'e';
		$method = 'get';
		$button = 'accesskey';
		$emo1 = '<img localsrc="180">';
		$emo2 = '<img localsrc="181">';
		$emo3 = '<img localsrc="182">';
		$emo4 = '<img localsrc="183">';
		$emo5 = '<img localsrc="184">';
		$meta = '<meta http-equiv="content-type" content="text/html; charset=Shift_JIS" />';

		require $ptel;

	# PC
	} else {
		$type = 'p';
		$method = 'post';
		$meta = '<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=Shift_JIS">';
		$meta .= "\n<STYLE TYPE=\"text/css\">\n<!--\n";
		$meta .= "body,tr,td,th { font-size:$b_size;font-family:\"$b_face\" }\n";
		$meta .= ".num { font-size:12px;font-family:Verdana,Helvetica,Arial; }\n";
		$meta .= "-->\n</STYLE>\n";

		require $pcom;
		require $jcode;
		$jflag++;
	}
}

#-------------------------------------------------
#  アクセス制限
#-------------------------------------------------
sub axscheck {
	# IP&ホスト取得
	$host = $ENV{'REMOTE_HOST'};
	$addr = $ENV{'REMOTE_ADDR'};

	if ($gethostbyaddr && ($host eq "" || $host eq $addr)) {
		$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2);
	}

	# IPチェック
	local($flg);
	foreach ( split(/\s+/, $deny_addr) ) {
		s/\./\\\./g;
		s/\*/\.\*/g;

		if ($addr =~ /^$_/i) { $flg = 1; last; }
	}
	if ($flg) {
		&error("アクセスを許可されていません");

	# ホストチェック
	} elsif ($host) {

		foreach ( split(/\s+/, $deny_host) ) {
			s/\./\\\./g;
			s/\*/\.\*/g;

			if ($host =~ /$_$/i) { $flg = 1; last; }
		}
		if ($flg) {
			&error("アクセスを許可されていません");
		}
	}
	if ($host eq "") { $host = $addr; }
}

#-------------------------------------------------
#  投稿受付
#-------------------------------------------------
sub regist {
	local($limit, $pwd, $time, @file, @w);

	# データ制限
	$limit = $maxdata / 2;
	if (length($in{'comment'}) > $maxdata) {
		&error("コメントは全角$limit字以内にしてください");
	}

	# チェック
	if ($no_wd) { &no_wd; }
	if ($jp_wd) { &jp_wd; }
	if ($urlnum > 0) { &urlnum; }

	# フォーム内容チェック
	local($err);
	if ($in{'name'} eq "") { $err .= "名前が未入力です<br>\n"; }
	if ($in{'comment'} eq "") { $err .= "コメントが未入力です<br>\n"; }
	if ($in{'email'} && $in{'email'} !~ /^[\w\.\-]+\@[\w\.\-]+\.[a-zA-Z]{2,6}$/) {
		$err .= "Ｅメールの入力内容が不正です<br>";
	}
	if ($err) { &error($err); }

	if ($in{'url'} eq "http://") { $in{'url'} = ""; }
	if ($in{'sub'} eq "") { $in{'sub'} = "無題"; }

	# ロック開始
	&lock if ($lockkey);

	# データ読み込み
	open(IN,"$logfile") || &error("Open Error: $logfile");
	@file = <IN>;
	close(IN);

	open(IN,"$numfile") || &error("Open Error: $numfile");
	$num = <IN>;
	close(IN);

	($no,$tim,$hos) = split(/<>/, $num);
	$no++;

	# 重複投稿チェック
	$time = time;

	# 同一ホスト連続投稿チェック
	if ($host eq $hos && $wait > $time - $tim) {
		&error("連続投稿はもうしばらく時間をおいて下さい");
	}

	# 削除キー暗号化
	if ($in{'pwd'} ne "") { $pwd = &encrypt($in{'pwd'}); }

	# 日時取得
	$date = &get_time($time, 'p');

	# 絵文字処理
	$in{'sub'}     = &emoji($in{'sub'});
	$in{'name'}    = &emoji($in{'name'});
	$in{'comment'} = &emoji($in{'comment'});

	# &#xxxxx; を復元
	$in{'sub'}     =~ s/\&amp;(#\d{5};)/\&$1/ig;
	$in{'name'}    =~ s/\&amp;(#\d{5};)/\&$1/ig;
	$in{'comment'} =~ s/\&amp;(#\d{5};)/\&$1/ig;

	# 独自形式を復元
	$in{'sub'}     =~ s/&lt;(if[0-9a-f][0-9a-f][0-9a-f])&gt;/<$1>/ig;
	$in{'name'}    =~ s/&lt;(if[0-9a-f][0-9a-f][0-9a-f])&gt;/<$1>/ig;
	$in{'comment'} =~ s/&lt;(if[0-9a-f][0-9a-f][0-9a-f])&gt;/<$1>/ig;

	# 親記事の場合
	if ($in{'res'} eq "") {

		$i = 0;
		$stop = 0;
		foreach (@file) {
			($no2,$reno2) = split(/<>/);
			$i++;
			if ($i > $max-1 && $reno2 eq "") { $stop=1; }
			if (!$stop) { push(@data,$_); }
		}
		unshift(@data,"$no<><>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>$type<>0<>\n");

	# レス記事の場合：トップソートあり
	} elsif ($in{'res'} && $topsort) {

		$f = 0;
		$oyaChk = 0;
		$match = 0;
		@data = ();
		@tmp = ();
		foreach (@file) {
			($no2,$reno2,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim,$typ,$res) = split(/<>/);

			if ($in{'res'} == $no2) {
				if ($reno2) { $f++; last; }
				$res++;
				$oyaChk++;
				$match=1;
				push(@data,"$no2<>$reno2<>$dat<>$nam<>$eml<>$sub<>$com<>$url<>$hos<>$pw<>$tim<>$typ<>$res<>\n");

			} elsif ($in{'res'} == $reno2) {
				push(@data,$_);

			} elsif ($match == 1 && $in{'res'} != $reno2) {
				$match=2;
				push(@data,"$no<>$in{'res'}<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>$type<><>\n");
				push(@tmp,$_);

			} else { push(@tmp,$_); }
		}
		if ($f) { &error("不正な返信要求です"); }
		if (!$oyaChk) { &error("親記事が存在しません"); }

		if ($match == 1) {
			push(@data,"$no<>$in{'res'}<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>$type<><>\n");
		}
		push(@data,@tmp);

	# レス記事の場合：トップソートなし
	} else {

		$f = 0;
		$oyaChk = 0;
		$match = 0;
		@data = ();
		foreach (@file) {
			($no2,$reno2,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim,$typ,$res) = split(/<>/);

			if ($in{'res'} == $no2) {
				$oyaChk++;
				$res++;
				$_ = "$no2<>$reno2<>$dat<>$nam<>$eml<>$sub<>$com<>$url<>$hos<>$pw<>$tim<>$typ<>$res<>\n";
			}
			if ($match == 0 && $in{'res'} == $no2) {
				if ($reno2) { $f++; last; }
				$match=1;

			} elsif ($match == 1 && $in{'res'} != $reno2) {
				$match=2;
				push(@data,"$no<>$in{'res'}<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>$type<><>\n");
			}
			push(@data,$_);
		}
		if ($f) { &error("不正な返信要求です"); }
		if (!$oyaChk) { &error("親記事が存在しません"); }

		if ($match == 1) {
			push(@data,"$no<>$in{'res'}<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>$type<><>\n");
		}
	}

	# 更新
	open(OUT,">$logfile") || &error("Write Error: $logfile");
	print OUT @data;
	close(OUT);

	open(OUT,">$numfile") || &error("Write Error: $numfile");
	print OUT "$no<>$time<>$host";
	close(OUT);

	# ロック解除
	&unlock if ($lockkey);

	# メール通知
	if ($mailing == 1 || ($mailing == 2 && $in{'email'} ne $mailto)) { &mail_to; }

	# 投稿後メッセージ
	if ($type eq 'p') {

		# クッキー記憶
		&set_cookie($in{'cook'},$in{'name'},$in{'email'},$in{'url'},$in{'pwd'});

		&header;
		print "<div align=\"center\"><hr width=400>\n";
		print "<h3>投稿は正常に処理されました</h3>\n";
		print "<form action=\"$script\">\n";
		print "<input type=submit value=\"掲示板へ戻る\"></form>\n";
		print "<hr width=400></div>\n";

	} else {
		&header;
		print "投稿完了<br><a href=\"$script?\">戻る</a>\n";
	}
	print "</body></html>\n";
	exit;
}

#-------------------------------------------------
#  ユーザ記事削除
#-------------------------------------------------
sub usrdel {
	local($no,$reno,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,
		$tim,$typ,$res,$match,$flag,$enpw,@data,@sort,%data,%res);

	if ($in{'no'} eq '') { &error("削除Noが入力モレです"); }
	if ($in{'pwd'} eq '') {	&error("削除キーが入力モレです"); }

	# ロック開始
	&lock if ($lockkey);

	$flag=0;
	open(IN,"$logfile") || &error("Open Error: $logfile");
	while (<IN>) {
		($no,$reno,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim,$typ,$res) = split(/<>/);

		if ($in{'no'} == $no) {
			$flag=1;
			$enpw = $pw;
			next;
		} elsif ($in{'no'} == $reno) {
			next;
		}

		$data{$no} = $_;
		if ($reno) { $res{$reno}++; }
		push(@sort,$no);
	}
	close(IN);

	if (!$flag) { &error("該当記事が見当たりません"); }
	if ($enpw eq '') { &error("記事に削除キーが設定されていません"); }

	# 削除キー照合
	$match = &decrypt($in{'pwd'}, $enpw);
	if ($match != 1) { &error("削除キーが違います"); }

	# データ整列
	foreach (@sort) {
		($no,$reno,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim,$typ,$res) = split(/<>/, $data{$_});

		push(@data,"$no<>$reno<>$dat<>$nam<>$eml<>$sub<>$com<>$url<>$hos<>$pw<>$tim<>$typ<>$res{$no}<>\n");
	}

	# ログ更新
	open(OUT,">$logfile") || &error("Write Error: $logfile");
	print OUT @data;
	close(OUT);

	# ロック解除
	&unlock if ($lockkey);
}

#-------------------------------------------------
#  フォームデコード
#-------------------------------------------------
sub decode {
	local($buf,$key,$val);

	if ($ENV{'REQUEST_METHOD'} eq "POST") {
		if ($ENV{'CONTENT_LENGTH'} > $maxData) {
			&error("投稿量が大きすぎます");
		}
		read(STDIN, $buf, $ENV{'CONTENT_LENGTH'});
	} else {
		$buf = $ENV{'QUERY_STRING'};
	}

	undef(%in);
	foreach ( split(/&/, $buf) ) {
		($key, $val) = split(/=/);
		$val =~ tr/+/ /;
		$val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg;

		# PCは文字コード変換
		&jcode'convert(*val, 'sjis') if ($type eq 'p');

		# エスケープ
		$val =~ s/&/&amp;/g;
		$val =~ s/"/&quot;/g;
		$val =~ s/</&lt;/g;
		$val =~ s/>/&gt;/g;
		$val =~ s/\r\n/<br>/g;
		$val =~ s/\r/<br>/g;
		$val =~ s/\n/<br>/g;

		$in{$key} .= "\0" if (defined($in{$key}));
		$in{$key} .= $val;
	}
	$page = $in{'page'};
	$mode = $in{'mode'};
	$headflag=0;

	# タイムゾーン設定
	$ENV{'TZ'} = "JST-9";
}

#-------------------------------------------------
#  HTMLヘッダ
#-------------------------------------------------
sub header {
	local($len) = @_;

	if ($headflag) { return; }

	local($head) = "<html><head>\n$meta\n";
	$head .= "<title>$title</title></head>\n";

	if ($type eq 'p') {
		$head .= "$body\n";
	} else {
		$head .= "<body>\n";
	}

	print "Content-Type: text/html\n";

	if ($cont_len && $len > 0) {
		$len += length($head);

		print "Content-Length: $len\n";
	}

	print "\n";
	print $head;

	$headflag=1;
}

#-------------------------------------------------
#  日時取得
#-------------------------------------------------
sub get_time {
	local($time, $key) = @_;
	local($date);

	# 時間取得
	local($min,$hour,$mday,$mon,$year,$wday) = (localtime($time))[1..6];

	# フォーマット
	if ($key eq 'p') {
		local(@wk) = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
		$date = sprintf("%04d/%02d/%02d(%s) %02d:%02d",
				$year+1900,$mon+1,$mday,$wk[$wday],$hour,$min);
	} else {
		$date = sprintf("%s/%s-%02d:%02d", $mon+1,$mday,$hour,$min);
	}
	$date;
}

#-------------------------------------------------
#  パスワード暗号化
#-------------------------------------------------
sub encrypt {
	local($inpw) = @_;
	local($salt, $crypt, @s);

	# 種生成
	@s = ('a'..'z', 'A'..'Z', '0'..'9', '.', '/');
	srand;
	$salt = $s[int(rand(@s))] . $s[int(rand(@s))];

	# 暗号化
	$crypt = crypt($inpw, $salt) || crypt ($inpw, '$1$' . $salt);
	$crypt;
}

#-------------------------------------------------
#  パスワード照合
#-------------------------------------------------
sub decrypt {
	local($inpw, $enpw) = @_;

	# 種取得
	local($salt) = $enpw =~ /^\$1\$(.*)\$/ && $1 || substr($enpw, 0, 2);

	# 照合
	if (crypt($inpw, $salt) eq $enpw || crypt($inpw, '$1$' . $salt) eq $enpw) {
		return (1);
	} else {
		return (0);
	}
}

#-------------------------------------------------
#  ロック処理
#-------------------------------------------------
sub lock {
	# リトライ定義
	local($retry) = 5;

	# 古いロックは削除
	if (-e $lockfile) {
		local($mtime) = (stat($lockfile))[9];
		if ($mtime < time - 30) { &unlock; }
	}

	# symlink関数式ロック
	if ($lockkey == 1) {
		while (!symlink(".", $lockfile)) {
			if (--$retry <= 0) { &error('LOCK is BUSY'); }
			sleep(1);
		}

	# mkdir関数式ロック
	} elsif ($lockkey == 2) {
		while (!mkdir($lockfile, 0755)) {
			if (--$retry <= 0) { &error('LOCK is BUSY'); }
			sleep(1);
		}
	}

	$lockflag=1;
}

#-------------------------------------------------
#  ロック解除
#-------------------------------------------------
sub unlock {
	if ($lockkey == 1) {
		unlink($lockfile);
	} elsif ($lockkey == 2) {
		rmdir($lockfile);
	}

	$lockflag=0;
}

#-------------------------------------------------
#  メール送信
#-------------------------------------------------
sub mail_to {
	local($msub,$mbody,$msg,$email);

	# JISコード変換
	if (!$jflag) { require $jcode; }

	# メールタイトルを定義
	$msub = &base64("[$title : $no] $in{'sub'}");

	# 本文内容編集
	$msg = $in{'comment'};
	$msg =~ s/<br>/\n/g;
	$msg =~ s/<([^>]|\n)*>//g;
	$msg =~ s/&lt;/＜/g;
	$msg =~ s/&gt;/＞/g;
	$msg =~ s/&amp;/＆/g;
	$msg =~ s/&quot;/”/g;

	$mbody = <<EOM;
$titleに以下の投稿がありました。

投稿日時：$date
ホスト名：$host
ブラウザ：$ENV{'HTTP_USER_AGENT'}

おなまえ：$in{'name'}
Ｅメール：$in{'email'}
タイトル：$in{'sub'}
ＵＲＬ  ：$in{'url'}

$msg
EOM

	# メールアドレスがない場合
	if ($in{'email'} eq "") { $email = $mailto; }
	else { $email = $in{'email'}; }

	# sendmail送信
	open(MAIL,"| $sendmail -t -i") || &error("メール送信失敗");
	print MAIL "To: $mailto\n";
	print MAIL "From: $email\n";
	print MAIL "Subject: $msub\n";
	print MAIL "MIME-Version: 1.0\n";
	print MAIL "Content-type: text/plain; charset=ISO-2022-JP\n";
	print MAIL "Content-Transfer-Encoding: 7bit\n";
	print MAIL "X-Mailer: $ver\n\n";
	foreach ( split(/\n/, $mbody) ) {
		&jcode'convert(*_, 'jis', 'sjis');
		print MAIL $_, "\n";
	}
	close(MAIL);
}

#-------------------------------------------------
#  BASE64変換
#-------------------------------------------------
#	とほほのWWW入門で公開されているルーチンを
#	参考にしました。( http://tohoho.wakusei.ne.jp/ )
sub base64 {
	local($sub) = @_;
	&jcode'convert(*sub, 'jis', 'sjis');

	$sub =~ s/\x1b\x28\x42/\x1b\x28\x4a/g;
	$sub = "=?iso-2022-jp?B?" . &b64enc($sub) . "?=";
	$sub;
}
sub b64enc {
	local($ch)="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	local($x, $y, $z, $i);
	$x = unpack("B*", $_[0]);
	for ($i=0; $y=substr($x,$i,6); $i+=6) {
		$z .= substr($ch, ord(pack("B*", "00" . $y)), 1);
		if (length($y) == 2) {
			$z .= "==";
		} elsif (length($y) == 4) {
			$z .= "=";
		}
	}
	$z;
}

#-------------------------------------------------
#  自動リンク
#-------------------------------------------------
sub auto_link {
	$_[0] =~ s/([^=^\"]|^)(http\:[\w\.\~\-\/\?\&\=\@\;\#\:\%\+]+)/$1<a href=\"$2\" target=\"_blank\">$2<\/a>/g;
}

#-------------------------------------------------
#  絵文字DB認識
#-------------------------------------------------
sub emoji_data {
	local($i,$j,$e,$e2);

	# ハッシュ初期化
	%i2e=(); %i2e2=();
	%e2i=(); %e2i2=();
	%i2j=(); %j2i=();
	%j2e=(); %j2e2=();

	# i2e
	open(IN,"$emodir/i2e.dat");
	while (<IN>) {
		chop;
		($i,$e,$e2) = split(/\t/);

		$i2e{$i}   = $e;
		$i2e2{$i}  = $e2;
		$e2i{$e}   = $i;
		$e2i2{$e2} = $i;
	}
	close(IN);

	# i2j
	open(IN,"$emodir/i2j.dat");
	while (<IN>) {
		chop;
		($i,$j) = split(/\t/);

		$i2j{$i} = $j;
		$j2i{$j} = $i;
	}
	close(IN);

	# j2e
	open(IN,"$emodir/j2e.dat");
	while (<IN>) {
		chop;
		($j,$e,$e2) = split(/\t/);

		$j2e{$j}  = $e;
		$j2e2{$j} = $e2;
	}
	close(IN);
}

#-------------------------------------------------
#  絵文字受理
#-------------------------------------------------
sub emoji {
	local($_) = @_;

	# 10進表記で投稿された場合 → 独自フォーマット化
	s/\&amp\;\#(63\d{3})\;/'<i'.sprintf("%x", $1).'>'/eg;

	# i-mode → 独自フォーマット化
	s/\G((?:[\x80-\x9F\xE0-\xF7\xFA-\xFC][\x40-\x7E\x80-\xFC]|[\x00-\x7F]|[\xA1-\xDF])*)([\xF8\xF9][\x40-\x7E\x80-\xFC])/$1.'<i'.unpack('H4', $2).'>'/ego;

	# vodafone → 独自フォーマット化
	s/(\x1B\x24[E-GO-Q][\x21-\x7A]+)\x0F/&j2o($1)/eg;

	# EZweb → 独自フォーマット化
	s/\G((?:[\x80-\x9F\xE0-\xF2\xF4-\xFC][\x40-\x7E\x80-\xFC]|[\x00-\x7F]|[\xA1-\xDF])*)([\xF3][\x40-\x7E\x80-\xFC])/$1.'<e'.unpack('H4', $2).'>'/ego;
	s/\G((?:[\x80-\x9F\xE0-\xF3\xF5-\xFC][\x40-\x7E\x80-\xFC]|[\x00-\x7F]|[\xA1-\xDF])*)([\xF4][\x40-\x7E\x80-\x8D])/$1.'<e'.unpack('H4', $2).'>'/ego;
	s/\G((?:[\x80-\x9F\xE0-\xF5\xF8-\xFC][\x40-\x7E\x80-\xFC]|[\x00-\x7F]|[\xA1-\xDF])*)([\xF6\xF7][\x40-\x7E\x80-\xFC])/$1.'<e'.unpack('H4', $2).'>'/ego;

	$_;
}

#-------------------------------------------------
#  絵文字 j → 独自
#-------------------------------------------------
sub j2o {
	local($_) = @_;

	# タグ復元
	s/&amp;/&/g;
	s/&quot;/"/g;
	s/&lt;/</g;
	s/&gt;/>/g;

	s/\x1B\x24[E-GO-Q]([^\x0F]+)\x0F/$1/;
	s/\x1B\x24([E-GO-Q])([\x21-\x7A]+)/&j2o2($1,$2)/eg;
	$_;
}
sub j2o2 {
	local($f1, $f2) = @_;

	$f2 =~ s/([\x21-\x7A])/'<j'.$f1.unpack('H2',$1).'>'/eg;
	$f2;
}

#-------------------------------------------------
#  絵文字 独自 → i,j,e
#-------------------------------------------------
sub o2ije {
	local($_) = @_;

	# i-mode
	s/<i(f)([89])(..)>/pack('H2H2', "$1$2", $3)/eg;

	# vodafone
	s/<j([E-GO-Q])(..)>/pack('H2H2','1B','24').$1.pack('H2',$2).pack('H2','0F')/eg;

	# EZweb
	s/<e(f)(.)(..)>/pack('H2H2', "$1$2", $3)/eg;

	$_;
}

#-------------------------------------------------
#  絵文字 独自 → PC
#-------------------------------------------------
sub o2p {
	local($_) = @_;

	# vodafone
	s|<j([E-GO-Q])(..)>|<img src=\"$img_j/$1$2\.gif\" align=top>|g;

	# i-mode
	s|<i(f)([89])(..)>|<img src=\"$img_i/$1$2$3\.gif\" align=top>|g;

	# EZweb
	s|<e(f)(...)>|<img src=\"$img_e/$1$2$3\.gif\" align=top>|g;

	$_;
}

#-------------------------------------------------
#  絵文字 i → j
#-------------------------------------------------
sub i2j {
	local($_) = @_;

	s/<i(f)([89])(..)>/&i2j2("$1$2$3")/eg;
	$_;
}
sub i2j2 {
	local($_) = @_;

	if (defined($i2j{$_})) {
		$e = $i2j{$_};
		$e =~ s/(.)(..)/pack('H2H2','1B','24').$1.pack('H2',$2).pack('H2','0F')/eg;
		$e;
	} else {
		return "<img src=\"$img_i/$_\.png\">";
	}
}

#-------------------------------------------------
#  絵文字 i → e
#-------------------------------------------------
sub i2e {
	local($_) = @_;

	s/<i(f)([89])(..)>/&i2e2("$1$2$3")/eg;
	$_;
}
sub i2e2 {
	local($_) = @_;

	if (defined($i2e{$_})) {
		return "<img localsrc=\"$i2e{$_}\">";
	} else {
		return "<img src=\"$img_i/$_\.gif\">";
	}
}

#-------------------------------------------------
#  絵文字 j → i
#-------------------------------------------------
sub j2i {
	local($_) = @_;

	s/<j([E-GO-Q])(..)>/&j2i2($1,$2)/eg;
	$_;
}
sub j2i2 {
	local($f1, $f2) = @_;

	if (defined($j2i{"$f1$f2"})) {
		local($e) = $j2i{"$f1$f2"};
		$e =~ s/(..)(..)/pack('H2H2', $1, $2)/e;
		return $e;
	} else {
		return "<img src=\"$img_j/$f1$f2\.gif\">";
	}
}

#-------------------------------------------------
#  絵文字 j → e
#-------------------------------------------------
sub j2e {
	local($_) = @_;

	s|<j([E-GO-Q])(..)>|&j2e2("$1$2")|eg;
	$_;
}
sub j2e2 {
	local($_) = @_;

	if (defined($j2e{$_})) {
		return "<img localsrc=\"$j2e{$_}\">";
	} else {
		return "<img src=\"$img_j/$_\.gif\">";
	}
}

#-------------------------------------------------
#  絵文字 e → i
#-------------------------------------------------
sub e2i {
	local($_) = @_;

	s|<e(f)(...)>|&e2i2("$1$2")|eg;
	$_;
}
sub e2i2 {
	local($_) = @_;

	if (defined($e2i2{$_})) {
		local($e) = $e2i2{$_};
		$e =~ s/(..)(..)/pack('H2H2', $1, $2)/e;
		return $e;
	} else {
		return "<img src=\"$img_e/$_\.gif\">";
	}
}

#-------------------------------------------------
#  絵文字 e → j
#-------------------------------------------------
sub e2j {
	local($_) = @_;

	s|<e(f)(...)>|&e2j2("$1$2")|eg;
	$_;
}
sub e2j2 {
	local($_) = @_;

	if (defined($e2j2{$_})) {
		$e = $e2j2{$_};
		$e =~ s/(.)(..)/pack('H2H2','1B','24').$1.pack('H2',$2).pack('H2','0F')/eg;
		$e;
	} else {
		return "<img src=\"$img_e/$_\.png\">";
	}
}

#-------------------------------------------------
#  禁止ワードチェック
#-------------------------------------------------
sub no_wd {
	local($flg);
	foreach ( split(/,/, $no_wd) ) {
		if (index("$in{'name'} $in{'sub'} $in{'comment'}",$_) >= 0) {
			$flg = 1; last;
		}
	}
	if ($flg) { &error("禁止ワードが含まれています"); }
}

#-------------------------------------------------
#  日本語チェック
#-------------------------------------------------
sub jp_wd {
	local($sub, $com, $mat1, $mat2, $code1, $code2);
	$sub = $in{'sub'};
	$com = $in{'comment'};
	if (!$jflag) { require $jcode; }
	if ($sub) {
		($mat1, $code1) = &jcode'getcode(*sub);
	}
	($mat2, $code2) = &jcode'getcode(*com);
	if ($code1 ne 'sjis' && $code2 ne 'sjis') {
		&error("題名又はコメントに日本語が含まれていません");
	}
}

#-------------------------------------------------
#  URL個数チェック
#-------------------------------------------------
sub urlnum {
	local($com) = $in{'comment'};
	local($num) = ($com =~ s|(https?://)|$1|ig);
	if ($num > $urlnum) {
		&error("コメント中のURLアドレスは最大$urlnum個までです");
	}
}

#-------------------------------------------------
#  チェックモード
#-------------------------------------------------
sub check {
	&header;
	print <<EOM;
<h2>チェックモード</h2>
<ul>
EOM

	local(%log) = ( $logfile, "ログファイル", $numfile, "記事Noファイル" );
	foreach ( $logfile, $numfile ) {
		if (-e $_) {
			print "<li>$log{$_}パス：OK\n";

			if (-r $_ && -w $_) {
				print "<li>$log{$_}パーミッション：OK\n";
			} else {
				print "<li>$log{$_}パーミッション：NG\n";
			}
		} else {
			print "<li>$log{$_}パス：NG → $_\n";
		}
	}

	# ロックディレクトリ
	print "<li>ロック形式：";
	if ($lockkey == 0) {
		print "ロック設定なし\n";
	} else {
		if ($lockkey == 1) {
			print "symlink\n";
		} else {
			print "mkdir\n";
		}

		local($lockdir) = $lockfile =~ /(.*)[\\\/].*$/;

		print "<li>ロックディレクトリ：$lockdir\n";

		if (-d $lockdir) {
			print "<li>ロックディレクトリのパス：OK\n";

			if (-r $lockdir && -w $lockdir && -x $lockdir) {
				print "<li>ロックディレクトリのパーミッション：OK\n";
			} else {
				print "<li>ロックディレクトリのパーミッション：NG → $lockdir\n";
			}
		} else {
			print "<li>ロックディレクトリのパス：NG → $lockdir\n";
		}
	}

	print <<EOM;
<li>バージョン：$ver
</ul>
</body>
</html>
EOM
	exit;
}


__END__

