#! /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"'; #メールアドレスのリンクに使用する画像のタグ #(メールアドレスが記入されていないとき) $mail_image02 = 'Mail"'; # ------------------------------------------------------------------------- #URLにリンクをはる(はる場合は'1'、はらない場合は''とする) $url_link = ''; # ------------------------------------------------------------------------- #画像にURLのリンクをはる #(はる場合は'1'、はらない場合は''とする) $image_url = '1'; #URLのリンクに使用する画像のタグ #(URLが記入されているとき) $url_image01 = 'URL'; #URLのリンクに使用する画像のタグ #(URLが記入されていないとき) $url_image02 = 'URL'; # ------------------------------------------------------------------------- #ロックするかどうか(使用しない場合は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; &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