#! /usr/bin/perl
# ---------------------------------------------------------------
# /////apeboard Ver.1.11(SJIS)/////
#
# Copyright (C) 1999,by 2apes.
# All rights reserved
# Script written by Taishi Yokoyama
#
# mail:nixx@2apes.com
#
# ---------------------------------------------------------------
# 初期設定
# ---------------------------------------------------------------
require './jcode.pl';
#管理者用のパスワード
$master_pwd = 'katukurumi';
#管理者のメールアドレス
$admin = 'kurumin@room.ne.jp';
#書き込みを管理者宛にメールで送る(送るときは'on'送らないときは'off')
$smail = 'off';
#メールを送るときのタイトルのあたまにつける文字列(例えば、apeboard)
$mail_head = 'apeboard';
#sendmailのパス
$sendmail = '/usr/lib/sendmail';
#ホームページのアドレス
$indexurl = 'http://kurumin.room.ne.jp/top/';
#設置するBBSのファイル名(このファイル名)
$thisurl = 'apeboard.cgi';
#データファイルの場所の指定
$datafile = './apeboard.dat';
#BBSのヘッダファイルの指定
$head_html = './header.html';
#BBSのログ部分のファイルの指定
$main_html = './main.html';
#BBSのフッタ(初期)ファイルの指定
$fot1_html = './footer1.html';
#BBDのフッタ(次ページ用)ファイルの指定
$fot2_html = './footer2.html';
#クッキーの名前(必ず変更)
$cookiename = 'kuruminBBS';
#クッキーの有効期限(日数)
$cookieday = '10';
#保存される最大記事件数
$max_data = '100';
#コメントの最大記入値(0なら無制限)
$maxlength = '0';
#表示される記事件数
$data_out = '15';
#名前の入力省略の可否(省略可のときは''、省略不可のときは'1')
$must_name = '1';
#メールアドレスの省略の可否(省略可のときは''、省略不可のときは'1')
$must_mail = '';
#削除用パスワードの省略の可否(省略可のときは''、省略不可のときは'1')
$must_pwd = '';
#プロキシを通しての投稿を禁止する。(規制するときは'1'、しないときは'')
$deny_proxy = '';
#メールアドレスにリンクをはる(はる場合は'1'、はらない場合は''とする)
$mail_link = '';
#名前にメールアドレスのリンクをはる
#(はる場合は'1'、はらない場合は''とする)
$name_link = '';
# -------------------------------------------------------------------------
#画像にメールアドレスのリンクをはる
#(はる場合は'1'、はらない場合は''とする)
$image_mail = '1';
#メールアドレスのリンクに使用する画像のタグ
#(メールアドレスが記入されているとき)
$mail_image01 = '
';
#メールアドレスのリンクに使用する画像のタグ
#(メールアドレスが記入されていないとき)
$mail_image02 = '
';
# -------------------------------------------------------------------------
#URLにリンクをはる(はる場合は'1'、はらない場合は''とする)
$url_link = '';
# -------------------------------------------------------------------------
#画像にURLのリンクをはる
#(はる場合は'1'、はらない場合は''とする)
$image_url = '1';
#URLのリンクに使用する画像のタグ
#(URLが記入されているとき)
$url_image01 = '
';
#URLのリンクに使用する画像のタグ
#(URLが記入されていないとき)
$url_image02 = '
';
# -------------------------------------------------------------------------
#ロックするかどうか(使用しない場合は0を入れる)
$lock = '1';
# ---------------------------------------------------------------
# 初期設定ここまで
# ---------------------------------------------------------------
&form_decord;
&get_cookie($cookiename);
$ck_name = $cookie{'name'};
$ck_mail = $cookie{'mail'};
$ck_url = $cookie{'url'};
$ck_pwd = $cookie{'pwd'};
$name = $FORMDATA{'name'};
$mail = $FORMDATA{'mail'};
$url = $FORMDATA{'url'};
$pwd = $FORMDATA{'pwd'};
$subject = $FORMDATA{'subject'};
$message = $FORMDATA{'message'};
$msgnum = $FORMDATA{'msgnum'};
$command = $FORMDATA{'command'};
$use_cookie = $FORMDATA{'use_cookie'};
$tgtid = $FORMDATA{'target'};
if ($url eq 'http://'){
$url = '';
}
if ($msgnum eq ''){
$msgnum = 0;
}
$name =~ s/\r|\n//g;
$mail =~ s/\r|\n//g;
$url =~ s/\r|\n//g;
$subject =~ s/\r|\n//g;
$message =~ s/(http:\/\/[\w\.\/\~\-\+\=\#\%\&\?\(\)]+)/$1<\/A>/ig;
$message =~ s/\r\n/
/g;
$message =~ s/\r|\n/
/g;
$message =~ s/(
)*$//;
if ($command eq 'read'){
&read_message;
} elsif ($command eq 'write'){
&write_message;
&read_message;
} elsif ($command eq 'remove'){
&remove_message;
&read_message;
} else {
&read_message;
}
exit(0);
# フォームデータをデコードするためのサブルーチン-----------------
sub form_decord{
local($query,@assocarray,$assoc,$property,$value);
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
} else {
$query= $ENV{'QUERY_STRING'};
}
@assocarray = split(/&/, $query);
foreach $assoc (@assocarray) {
($property, $value) = split(/=/, $assoc);
$value =~ tr/+/ /;
$value =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack("C", hex($1))/eg;
$value =~ s/&/&/g;
$value =~ s/"/"/g;
$value =~ s/</g;
$value =~ s/>/>/g;
$value =~ s/,/,/g;
&jcode'convert(*value,'sjis');
&jcode'h2z_sjis(*value);
if ($property eq 'target'){
push(@RM,$value);
}else{
$FORMDATA{$property} = $value;
}
}
}
# メッセージを記録するためのサブルーチン-------------------------
sub write_message{
local($miss) = '';
local($encpwd,$i);
# 入力事項のチェック-------------------------------
if ($must_name ne '' && $name eq '') {
if ($miss) {
$miss .= 'と名前';
} else {
$miss .= '名前';
}
}
if ($must_mail ne '' && $mail eq '') {
if ($miss) {
$miss .= 'とメールアドレス';
} else {
$miss .= 'メールアドレス';
}
}
if ($message eq '') {
if ($miss) {
$miss .= 'とコメント';
} else {
$miss .= 'コメント';
}
}
if ($miss) {
&print_error("$missが入力されていません。");
}
if ($must_mail ne '' && $mail !~ /.+\@.+\..+/) {
&print_error('メールアドレスが不正です。');
}
if ($maxlength > 0) {
if (length($message) > $maxlength) {
&print_error('書き込みが長すぎます。');
}
}
if ($must_pwd ne ''){
if ($pwd eq '' || length($pwd) < 4) {
&print_error('パスワードが入力されていないか、短すぎます。');
}
}
&lock_open(TXT, "+<$datafile");
@txt = ;
$encoded_pass = splice(@txt, 0, 1);
chop($encoded_pass);
seek(TXT, 0, 0);
print TXT "$encoded_pass\n";
# 二重投稿のチェック-------------------------------
for ($i = 0; $i < 5; $i++) {
($dispid,$dispname,$dispmail,$dispurl,$dispdate,$disppwd,$dispsubject,$dispmsg,$dispres) = split(/,/, $txt[$i]);
if ($dispmsg eq $message) {
&unlock_close(TXT);
&print_error("同じ内容で書き込もうとしています。戻ってチェックしてみて下さい。");
}
}
$datestr = &get_date_string;
# メールで送信-------------------------------------
if ($smail eq 'on'){
$mail_subject = "[$mail_head]$FORMDATA{'subject'}";
if ($subject eq ''){
$mail_subject = '[$mail_head]No Subject';
}
$mail_msg = substr("__ $mail_head ______________________________________________________",0,60)."\n\n";
$mail_msg .= "$FORMDATA{'name'}さん\n\n";
$mail_msg .= "$FORMDATA{'message'}\n\n";
$mail_msg .= "$FORMDATA{'url'}\n";
$mail_msg .= "____________________________________________________________\n";
$mail_msg =~ s/\r/\n/g;
&jcode'convert(*mail_subject,'jis');
&jcode'convert(*mail_msg,'jis');
if (!open(MAIL,"| $sendmail -t -n -oi $admin")){
&print_error(bad_sendmail);
}
print MAIL "To: $admin\n";
print MAIL "From: $admin\n";
print MAIL "Subject: $mail_subject\n";
print MAIL "Content-Transfer-Encoding: 7bit\n";
print MAIL "Content-Type: text/plain;\n\n";
print MAIL $mail_msg;
print MAIL "\n";
close(MAIL);
}
# 記事番号の設定-----------------------------------
$id = 1;
for ($i = 0; $i < @txt; $i++) {
($thisid) = split(/,/, $txt[$i]);
if ($thisid >= $id) {
$id = $thisid + 1;
}
}
# 書き込む情報の整列-------------------------------
if ($must_pwd ne ''){
$encpwd = &encode_pwd($pwd);
} else {
$encpwd = $encoded_pass;
}
$oneline = "$id,$name,$mail,$url,$datestr,$encpwd,$subject,$message,\n";
unshift(@txt, $oneline);
splice(@txt, $max_data);
print TXT @txt;
truncate(TXT, tell(TXT));
&unlock_close(TXT);
}
# メッセージを表示するためのサブルーチン-------------------------
sub read_message {
if ($deny_proxy ne ''){
$host = $ENV{'REMOTE_HOST'};
if($host =~ m/proxy/i){
&print_error("あなたのホスト $host は投稿制限の対象となっています。");
}
}
&lock_open(TXT, "+<$datafile");
@txt = ;
($encoded_pass) = splice(@txt, 0, 1);
chop($encoded_pass);
if ($encoded_pass eq '' || &mismatch_password($master_pwd, $encoded_pass)) {
$encmaster = &encode_pwd($master_pwd);
seek(TXT, 0, 0);
print TXT "$encmaster\n";
}
&unlock_close(TXT);
$volume = scalar(@txt);
# ヘッダ用のHTMLファイルを開く---------------------
open(HEAD, $head_html) || &print_error('ヘッダHTMLファイルが開けません。');
@headhtml = ;
close(HEAD);
# メインのHTMLファイルを開く-----------------------
open(MAIN, $main_html) || &print_error('メインHTMLファイルが開けません。');
@mainhtml = ;
close(MAIN);
# フッタ用のファイルを開く-------------------------
if ($data_out + $msgnum + 1 > $volume) {
open(FOOT, $fot1_html) || &print_error('フッタ用HTMLファイル1が開けません。');
@foothtml = ;
close(FOOT);
} else {
open(FOOT, $fot2_html) || &print_error('フッタ用HTMLファイル2が開けません。');
@foothtml = ;
close(FOOT);
}
# 画面に表示する前の設定---------------------------
print "Content-type: text/html\n";
print "Pragma: no-cache\n";
if ($command eq 'write' && $use_cookie eq 'on') {
undef %cookie;
$cookie{'name'} = $name;
$cookie{'mail'} = $mail;
$cookie{'url'} = $url;
$cookie{'pwd'} = $pwd;
&print_cookie($cookiename, $cookieday);
$ck_name = $name;
$ck_mail = $mail;
$ck_url = $url;
$ck_pwd = $pwd;
}
if ($ck_url eq '') {
$ck_url = 'http://';
}
print "\n";
# ヘッダの作成と変換-------------------------------
foreach $line1 (@headhtml) {
$line1 =~ s/(NAME="name")/$1 VALUE="$ck_name"/i;
$line1 =~ s/(NAME="mail")/$1 VALUE="$ck_mail"/i;
$line1 =~ s/(NAME="url")/$1 VALUE="$ck_url"/i;
$line1 =~ s/(NAME="pwd")/$1 Value="$ck_pwd"/i;
print $line1;
}
# 表示範囲の設定-----------------------------------
$msgstart = $msgnum;
if ($msgstart < 0) {
$msgstart = 0;
}
$msgend = $msgnum + $data_out;
if ($msgend > $volume) {
$msgend = $volume;
}
# 記事の表示---------------------------------------
for ($i = $msgstart; $i < $msgend; $i++) {
@article = split(/,/, $txt[$i]);
($dispid,$dispname,$dispmail,$dispurl,$dispdate,$disppwd,$dispsubject,$dispmsg,$dispres) = @article;
#メールアドレスにリンクをはる
if ($dispmail ne '' && $mail_link ne ''){
$dispmail = "$dispmail";
}
#名前にメールアドレスのリンクをはる
if ($dispmail ne '' && $name_link ne ''){
$dispname = "$dispname";
}
#画像にメールアドレスのリンクをはる
if ($dispmail ne '' && $image_mail ne ''){
$dispmail = "" . $mail_image01 . '';
} elsif ($dispmail eq '' && $image_mail ne ''){
$dispmail = $mail_image02;
}
#URLにリンクをはる
if ($dispurl ne '' && $url_link ne ''){
$dispurl = "$dispurl";
}
#画像にURLのリンクをはる
if ($dispurl ne '' && $image_url ne ''){
$dispurl = "" . $url_image01 . '';
} elsif ($dispurl eq '' && $image_url ne ''){
$dispurl = $url_image02;
}
# メイン部分の作成と文字列の変換---------------
@tmpdata = @mainhtml;
foreach $line2 (@tmpdata) {
$line2 =~ s/(NAME="target")/$1 VALUE="$dispid"/i;
$line2 =~ s/name/$dispname/i;
$line2 =~ s/URL/$dispurl/i;
$line2 =~ s/subject/$dispsubject/i;
$line2 =~ s/message/$dispmsg/i;
$line2 =~ s/date/$dispdate/i;
$line2 =~ s/mail/$dispmail/i;
$line2 =~ s/res/$dispres/i;
print $line2;
}
}
# フッタの作成と文字列の変換-----------------------
$nextmsg = $msgend ;
foreach $line3 (@foothtml) {
$line3 =~ s/(NAME="pwd")/$1 Value="$ck_pwd"/i;
$line3 =~ s/----N----/$msgend/i;
$line3 =~ s/(A HREF="back")/A HREF="$thisurl?command=read_message&msgnum=$nextmsg"/i;
$line3 =~ s/(A HREF="top")/A HREF="$thisurl"/i;
print $line3;
}
}
# メッセージを削除するサブルーチン-------------------------------
sub remove_message {
&lock_open(TXT, "+<$datafile");
@txt = ;
($encoded_pass) = splice(@txt, 0, 1);
chop($encoded_pass);
@tmp = @RM;
foreach $tgtid(@tmp){
$index = &find_msg;
if ($index < 0) {
&unlock_close(TXT);
&print_error("そのメッセージは存在しません。");
}
($dispid,$dispname,$dispmail,$dispurl,$dispdate,$disppwd,$dispsubject,$dispmsg,$dispres) = split(/,/, $txt[$index]);
if (&mismatch_password($pwd, $disppwd) && &mismatch_password($pwd, $encoded_pass)) {
&unlock_close(TXT);
&print_error("パスワードが不適切で削除できません");
}
splice(@txt, $index, 1);
}
unshift(@txt, ("$encoded_pass\n"));
seek(TXT, 0, 0);
print TXT @txt;
truncate(TXT, tell(TXT));
&unlock_close(TXT);
}
# ファイルロックのサブルーチン-----------------------------------
sub lock_open {
local(*FILE, $lk_name) = @_;
if (!open(FILE, $lk_name)) {
&print_error("$lk_nameがオープンできません。");
}
if ($lock) {
eval("flock(FILE, 2)"); # 2=LOCK_EX
if ($@) {
&print_error("$@ - この環境では flock は使えません。\$uselock = 0 にしてください。");
}
}
seek(FILE, 0, 0);
}
#ファイルアンロックのサブルーチン-------------------------------
sub unlock_close {
local(*FILE) = @_;
if ($lock) {
eval("flock(FILE, 8)"); # 8=LOCK_UN
}
close(FILE);
}
# 現在日時を得る-------------------------------------------------
sub get_date_string {
local(@week) = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
local($sec, $min, $hour, $day, $mon, $year, $weekday) = localtime(time);
$year += 1900;
$mon++;
# 文字列化する-------------------------------------
if ($hour < 10) {
$hour = "0$hour";
}
if ($min < 10) {
$min = "0$min";
}
if ($sec < 10) {
$sec = "0$sec";
}
$weekstr = $week[$weekday];
return "$year/$mon/$day ($weekstr) $hour:$min";
}
# パスワードの暗号化---------------------------------------------
sub encode_pwd {
local($sec, $min, $hour, $day, $mon, $year, $weekday) = localtime(time);
local(@token) = ('0'..'9', 'A'..'Z', 'a'..'z');
local($pass) = @_;
local($encpass, $salt1, $salt2);
$salt1 = $token[(time | $$) % scalar(@token)];
$salt2 = $token[($sec + $min*60 + $hour*60*60) % scalar(@token)];
$encpass = crypt($pass, "$salt1$salt2");
return $encpass;
}
# クッキーの有効期限の設定---------------------------------------
sub get_expire_date_string {
local($days) = @_;
local(@month) = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
local(@week) = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" );
local($sec, $min, $hour, $day, $mon, $year, $weekday) = gmtime(time + $days * 24 * 60 * 60);
local($expiredate);
$year += 1900;
# 文字列化する-------------------------------------
if ($hour < 10) {
$hour = "0$hour";
}
if ($min < 10) {
$min = "0$min";
}
if ($sec < 10) {
$sec = "0$sec";
}
$weekstr = $week[$weekday];
$monstr = $month[$mon];
$expiredate = "$weekstr, $day-$monstr-$year $hour:$min:$sec GMT";
return $expiredate;
}
# クッキー取得のサブルーチン-------------------------------------
sub get_cookie {
local($cookiename) = @_;
local($key, $value, @pairs, $pair);
@sqpairs = split(/;\s/, $ENV{'HTTP_COOKIE'});
foreach $sqpair (@sqpairs) {
($sqkey, $sqvalue) = split(/=/, $sqpair);
if ($sqkey eq $cookiename) {
$sqvalue =~ s/:/; /g;
$sqvalue =~ s/_/=/g;
@pairs = split(/;\s/, $sqvalue);
foreach $pair (@pairs) {
($key, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$key =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
$value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
$cookie{$key} = $value;
}
last;
}
}
}
# クッキーを作るサブルーチン-------------------------------------
sub make_cookie {
local($cookiename) = @_;
local(@sqcookie, $sqstr);
local($encode) = '\%\+\;\,\=\&\_\:';
while (($key, $value) = each %cookie) {
$key =~ s/([$encode])/'%'.unpack("H2", $1)/eg;
$value =~ s/([$encode])/'%'.unpack("H2", $1)/eg;
$key =~ s/\s/\+/g;
$value =~ s/\s/\+/g;
push(@sqcookie, "${key}_${value}");
}
$sqstr = join(':', @sqcookie);
return "$cookiename=$sqstr; ";
}
# クッキー表示のサブルーチン-------------------------------------
sub print_cookie {
local($cookiename, $days, $domain) = @_;
local($cookiestr) = &make_cookie($cookiename);
local($expdate) = &get_expire_date_string($days);
print "Set-Cookie: $cookiestr;";
print " expires=$expdate;";
if ($domain) {
print " domain=$domain;";
}
print "\n";
}
# メッセージを探すサブルーチン-----------------------------------
sub find_msg {
local($i, $foundindex, $dispid);
$foundindex = -1;
for ($i = 0; $i < @txt; $i++) {
($dispid) = split(/,/, $txt[$i]);
if ($tgtid == $dispid) {
$foundindex = $i;
last;
}
}
return $foundindex;
}
# パスワードの照合-----------------------------------------------
sub mismatch_password {
local($pass, $encodedpass) = @_;
if ($encodedpass ne crypt($pass, $encodedpass)) {
return 1;
} else {
return 0;
}
}
# エラー表示のサブルーチン---------------------------------------
sub print_error {
local($msg) = @_;
print "Content-type: text/html\n";
print "\n";
print "\n";
print "\n";
print "\n";
print "$msg\n";
print "\n";
print "$msg
\n";
print "再度試していただくか、";
print "$admin";
print "までお知らせください。\n";
print "
\n";
print "戻る\n";
print "
\n";
print "ホームページへ戻る\n";
print "
\n";
print "\n";
exit(0);
}
# End of Script