#!/var/www/vhosts/hairsyndicate.com/cgi-bin/ #┌───────────────────────────────── #│ Aska BBS #│ aska.cgi - 2008/01/04 #│ Copyright (c) KentWeb #│ webmaster@kent-web.com #│ http://www.kent-web.com/ #└───────────────────────────────── # 外部ファイル取り込み require './init.cgi'; require $jcode; &decode; &axscheck; if ($in{'usrdel'}) { &usrdel; } elsif ($mode eq 'regist') { ®ist; } elsif ($mode eq 'find') { &find; } elsif ($mode eq 'howto') { &howto; } elsif ($mode eq 'admin') { &admin; } elsif ($mode eq 'check') { ✓ } &html_log; #------------------------------------------------- # アクセス制限 #------------------------------------------------- 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チェック my $flg; foreach ( split(/\s+/, $deny_addr) ) { s/\./\\\./g; s/\*/\.\*/g; if ($addr =~ /^$_/i) { $flg = 1; last; } } if ($flg) { &error("アクセスを許可されていません"); } # ホストチェック foreach ( split(/\s+/, $deny_host) ) { s/\./\\\./g; s/\*/\.\*/g; if ($host =~ /$_$/i) { $flg = 1; last; } } if ($flg) { &error("アクセスを許可されていません"); } if ($host eq "") { $host = $addr; } } #------------------------------------------------- # 記事表示 #------------------------------------------------- sub html_log { # 繰越ページ my $page = 0; foreach ( keys(%in) ) { if (/^page:(\d+)$/) { $page = $1; last; } } # クッキー取得 my ($cnam, $ceml, $curl, $cpwd) = &get_cookie; if (!$curl) { $curl = 'http://'; } # レス処理 $in{'res'} =~ s/\D//g; my ($r_sub, $r_com); if ($in{'res'}) { my ($flg, $no, $dat, $nam, $eml, $sub, $com); open(IN,"$logfile"); while () { ($no,$dat,$nam,$eml,$sub,$com) = split(/<>/); if ($in{'res'} == $no) { $flg = 1; last; } } close(IN); if (!$flg) { &error("該当記事が見つかりません"); } $sub =~ s/^Re://g; $sub =~ s/\[\d+\]\s?//g; $r_sub = "Re:[$no] $sub"; $r_com = "> $com"; $r_com =~ s/
/\n> /ig; } &header; print qq|
\n|; print "

$banner1

\n" if ($banner1 ne ""); # タイトル if ($ImgT) { print "\"$title\"\n"; } else { print "$title\n"; } print < [トップに戻る] [留意事項] [ワード検索] [管理用]
EOM # 投稿キー if ($regist_key) { require $regkeypl; my ($str_plain, $str_crypt) = &pcp_makekey; print qq||; print qq|\n|; print qq|\n|; } print <
おなまえ
Eメール
タイトル
メッセージ
参照先
削除キー (英数字で8文字以内)
投稿キー\n|; print qq|(投稿時 投稿キー を入力してください)
クッキー情報保存
EOM my $i = 0; open(IN,"$logfile") || &error("Open Error: $logfile"); while () { $i++; next if ($i < $page + 1); next if ($i > $page + $pageLog); my ($no,$date,$nam,$eml,$sub,$com,$url) = split(/<>/); if ($eml) { $nam = "$nam"; } if ($autolink) { &auto_link($com); } if ($refCol) { $com =~ s/([\>]|^)(>[^<]*)/$1$2<\/font>/g; } print qq|

[$no] $sub\n|; print qq|投稿者:$nam 投稿日:$date\n|; print qq|[返信]

\n|; print qq|
$com\n|; print qq|

$url

| if ($url); print qq|

\n|; } close(IN); print <
EOM # ページ繰り越し my $next = $page + $pageLog; my $back = $page - $pageLog; if ($back >= 0) { print "\n"; } if ($next < $i) { print "\n"; } # 著作権表示(削除禁止) print < 記事No 削除キー

$banner2

- ASKA BBS -
EOM exit; } #------------------------------------------------- # 記事書込 #------------------------------------------------- sub regist { # 投稿チェック if ($postonly && !$post_flag) { &error("不正なアクセスです"); } if ($baseUrl) { &refCheck; } # チェック if ($no_wd) { &no_wd; } if ($jp_wd) { &jp_wd; } if ($urlnum > 0) { &urlnum; } # 投稿キーチェック if ($regist_key) { require $regkeypl; if ($in{'regikey'} !~ /^\d{4}$/) { &error("投稿キーが入力不備です。
投稿フォームに戻って再読込み後、指定の数字を入力してください"); } # 投稿キーチェック # -1 : キー不一致 # 0 : 制限時間オーバー # 1 : キー一致 local($chk) = ®istkey_chk($in{'regikey'}, $in{'str_crypt'}); if ($chk == 0) { &error("投稿キーが制限時間を超過しました。
投稿フォームに戻って再読込み後、指定の数字を再入力してください"); } elsif ($chk == -1) { &error("投稿キーが不正です。
投稿フォームに戻って再読込み後、指定の数字を入力してください"); } } # フォーム内容をチェック local($err); if ($in{'name'} eq "") { $err .= "名前が入力されていません
"; } if ($in{'comment'} eq "") { $err .= "コメントが入力されていません
"; } if ($in{'email'} && $in{'email'}!~ /^[\w\.\-]+\@[\w\.\-]+\.[a-zA-Z]{2,6}$/) { $err .= "Eメールの入力内容が不正です
"; } if ($err) { &error($err); } if ($in{'url'} eq "http://") { $in{'url'} = ""; } if ($in{'sub'} eq "") { $in{'sub'} = "無題"; } # 先頭記事読み取り open(DAT,"+< $logfile") || &error("Open Error: $logfile"); eval 'flock(DAT, 2);'; my $top = ; # 重複投稿チェック my ($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim) = split(/<>/, $top); if ($in{'name'} eq $nam && $in{'comment'} eq $com) { close(DAT); &error("二重投稿は禁止です"); } # 連続投稿チェック my $time = time; my $flg; if ($regCtl == 1) { if ($host eq $hos && $time - $tim < $wait) { $flg = 1; } } elsif ($regCtl == 2) { if ($time - $tim < $wait) { $flg = 1; } } if ($flg) { close(DAT); &error("現在投稿制限中です。もうしばらくたってから投稿をお願いします"); } # 記事No採番 $no++; # 削除キー暗号化 my $pwd; if ($in{'pwd'} ne "") { $pwd = &encrypt($in{'pwd'}); } # 時間取得 my ($min,$hour,$mday,$mon,$year,$wday) = (localtime($time))[1..6]; my @wk = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); my $date = sprintf("%04d/%02d/%02d(%s) %02d:%02d", $year+1900,$mon+1,$mday,$wk[$wday],$hour,$min); # 記事数調整 my @data = ($top); my $i = 0; while () { $i++; push(@data,$_); last if ($i >= $max-1); } # 更新 seek(DAT, 0, 0); print DAT "$no<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>\n"; print DAT @data; truncate(DAT, tell(DAT)); close(DAT); # クッキーを記憶 if ($in{'cook'} eq 'on') { &set_cookie($in{'name'}, $in{'email'}, $in{'url'}, $in{'pwd'}); } # メール通知処理 if ($mailing == 1 || ($mailing == 2 && $in{'email'} ne $mailto)) { &mail_to; } # リロード if ($location) { if ($ENV{'PERLXS'} eq "PerlIS") { print "HTTP/1.0 302 Temporary Redirection\r\n"; print "Content-type: text/html\n"; } print "Location: $location?\n\n"; exit; } else { &message("投稿は正常に受理されました"); } } #------------------------------------------------- # ワード検索 #------------------------------------------------- sub find { &header; print <
  • キーワードを入力し、検索ボタンを押してください。
  • キーワードはスペースで区切って複数指定することができます。
    キーワード 条件 表\示
EOM # ワード検索の実行と結果表示 if ($in{'word'} ne "") { # 繰越ページ my $page = 0; foreach ( keys(%in) ) { if (/^page:(\d+)$/) { $page = $1; llast; } } # 入力内容を整理 $in{'word'} =~ s/\x81\x40/ /g; my @wd = split(/\s+/, $in{'word'}); # 検索 print "
\n"; my $i = 0; my @find; open(IN,"$logfile") || &error("Open Error: $logfile"); while () { my ($no,$dat,$nam,$eml,$sub,$com,$url) = split(/<>/); my $flg; foreach $wd (@wd) { if (index("$no $nam $eml $sub $com $url",$wd) >= 0) { $flg = 1; if ($in{'cond'} eq 'OR') { last; } } else { if ($in{'cond'} eq 'AND') { $flg = 0; last; } } } if ($flg) { $i++; next if ($i < $page + 1); next if ($i > $page + $in{'view'}); push(@find,$_); } } close(IN); print "
$in{'word'} に関連する記事は$i件見つかりました。\n"; foreach (@find) { my ($no,$ymd,$nam,$eml,$sub,$com,$url) = split(/<>/); if ($eml) { $nam = "$nam"; } if ($url) { $com .= "

$url

"; } print "

[$no] $sub "; print "投稿者:$nam 投稿日:$ymd

\n"; print "
$com

\n"; } print "

\n"; my $next = $page + $in{'view'}; my $back = $page - $in{'view'}; if ($back >= 0) { print "\n"; } if ($next < $i) { print "\n"; } } print < EOM exit; } #------------------------------------------------- # 管理モード #------------------------------------------------- sub admin { # 認証 if ($in{'pass'} eq "") { &enter_form; } elsif ($in{'pass'} ne $pass) { &error("パスワードが違います"); } # 削除処理 if ($in{'job'} eq "dele" && $in{'no'}) { # 削除情報 my %del; foreach ( split(/\0/, $in{'no'}) ) { $del{$_}++; } # 削除情報をマッチング my @data; open(DAT,"+< $logfile") || &error("Open Error: $logfile"); eval 'flock(DAT, 2);'; while () { my ($no,$dat,$nam,$eml,$sub,$com,$url) = split(/<>/); if (!defined($del{$no})) { push(@data,$_); } } # 更新 seek(DAT, 0, 0); print DAT @data; truncate(DAT, tell(DAT)); close(DAT); # 修正画面 } elsif ($in{'job'} eq "edit" && $in{'no'}) { if ($in{'no'} =~ /\0/) { &error("修正の場合選択する記事は1つのみです"); } # 記事抽出 local($no,$dat,$nam,$eml,$sub,$com,$url); open(IN,"$logfile") || &error("Open Error: $logfile"); while () { ($no,$dat,$nam,$eml,$sub,$com,$url) = split(/<>/); last if ($in{'no'} == $no); } close(IN); # 修正フォームへ &edit_form; # 修正実行 } elsif ($in{'job'} eq "edit2") { if ($in{'url'} eq "http://") { $in{'url'} = ""; } if ($in{'sub'} eq "") { $in{'sub'} = "無題"; } # 読み出し my @data; open(DAT,"+< $logfile") || &error("Open Error: $logfile"); eval 'flock(DAT, 2);'; while () { my ($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pwd,$tim) = split(/<>/); if ($in{'no'} == $no) { $_ = "$no<>$dat<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$hos<>$pwd<>$tim<>\n"; } push(@data,$_); } # 更新 seek(DAT, 0, 0); print DAT @data; truncate(DAT, tell(DAT)); close(DAT); # 完了メッセージ &message("記事を修正しました"); } # 削除画面を表示 &header; print <
  • 処理を選択して送信ボタンを押してください。
処理:
EOM # 記事を展開 open(IN,"$logfile") || &error("Open Error: $logfile"); while () { my ($no,$dat,$nam,$eml,$sub,$com,$url,$hos) = split(/<>/); if ($eml) { $nam="$nam"; } $com =~ s/<[^>]*(>|$)//g; if (length($com) > 60) { $com = substr($com,0,60) . '...'; } print qq|

|; print qq|[$no] $sub $nam - $dat\n|; print qq|【$hos】\n|; print qq|
$com\n|; } close(IN); print <
EOM exit; } #------------------------------------------------- # 修正フォーム #------------------------------------------------- sub edit_form { $com =~ s/
/\n/g; if (!$url) { $url = "http://"; } &header; print <
  • 変更する部分のみ修正して送信ボタンを押してください。
おなまえ
Eメール
タイトル
参照先
メッセージ

EOM exit; } #------------------------------------------------- # 留意事項 #------------------------------------------------- sub howto { &header; print <

留意事項

  1. この掲示板はクッキー対応です。一度記事を投稿いただくと、おなまえ、Eメール、URL、削除キーの情報は2回目以降は自動入力されます。(ただし利用者のブラウザがクッキー対応の場合)

  2. 投稿記事には、タグは一切使用できません。

  3. 記事を投稿する上での必須入力項目は「おなまえ」「メッセージ」です。Eメール、URL、題名、削除キーは任意です。

  4. 記事には、半角カナは一切使用しないで下さい。文字化けの原因となります。

  5. 記事の投稿時に「削除キー」にパスワード(英数字で8文字以内)を入れておくと、その記事は次回削除キーによって削除することができます。

  6. 記事の保持件数は最大$max件です。それを超えると古い順に自動削除されます。

  7. 既存の記事に簡単に「返信」することができます。各記事にある「返信」のリンク部を押すと投稿フォームが返信用となります。

  8. 過去の投稿記事から「キーワード」によって簡易検索ができます。トップメニューの「ワード検索」のリンクをクリックすると検索モードとなります。

  9. 管理者が著しく不利益と判断する記事や他人を誹謗中傷する記事は予\告なく削除することがあります。


EOM exit; } #------------------------------------------------- # ユーザ記事削除 #------------------------------------------------- sub usrdel { # 投稿チェック if ($postonly && !$post_flag) { &error("不正なアクセスです"); } if ($baseUrl) { &refCheck; } if ($in{'no'} eq '' || $in{'pwd'} eq '') { &error("削除Noまたは削除キーが入力モレです"); } my ($flg, @data); open(DAT,"+< $logfile") || &error("Open Error: $logfile"); eval 'flock(DAT, 2);'; while () { my ($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw) = split(/<>/); if ($in{'no'} == $no) { $flg = 1; # 削除キーなし if (!$pw) { $flg = -1; last; # 削除キー不一致 } elsif (&decrypt($in{'pwd'}, $pw) != 1) { $flg = -2; last; } next; } push(@data,$_); } # 判定 if ($flg == -1) { close(DAT); &error("この記事は削除キーが設定されていません"); } elsif (!$flg || $flg == -2) { close(DAT); &error("該当キーが認証できません"); } # ログを更新 seek(DAT, 0, 0); print DAT @data; truncate(DAT, tell(DAT)); close(DAT); # 完了メッセージ &message("記事を削除しました"); } #------------------------------------------------- # フォームデコード #------------------------------------------------- sub decode { my $buf; if ($ENV{'REQUEST_METHOD'} eq "POST") { $post_flag = 1; if ($ENV{'CONTENT_LENGTH'} > $maxData) { &error("投稿量が大きすぎます"); } read(STDIN, $buf, $ENV{'CONTENT_LENGTH'}); } else { $post_flag = 0; $buf = $ENV{'QUERY_STRING'}; } undef(%in); foreach ( split(/&/, $buf) ) { my ($key, $val) = split(/=/); $key =~ tr/+/ /; $key =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg; $val =~ tr/+/ /; $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg; # S-JISコード変換 &jcode::convert(\$val, "sjis", "", "z"); # エスケープ $val =~ s/&/&/g; $val =~ s/"/"/g; $val =~ s//>/g; $val =~ s/\r\n/
/g; $val =~ s/\r/
/g; $val =~ s/\n/
/g; $in{$key} .= "\0" if (defined($in{$key})); $in{$key} .= $val; } $mode = $in{'mode'}; # タイムゾーン設定 $ENV{'TZ'} = "JST-9"; $headflag = 0; } #------------------------------------------------- # HTMLヘッダ #------------------------------------------------- sub header { if ($headflag) { return; } print "Content-type: text/html\n\n"; print <<"EOM"; $title $body EOM $headflag = 1; } #------------------------------------------------- # エラー処理 #------------------------------------------------- sub error { my $msg = shift; &header; print <

ERROR !

$msg

EOM exit; } #------------------------------------------------- # クッキー発行 #------------------------------------------------- sub set_cookie { my @cook = @_; my @t = gmtime(time + 60*24*60*60); my @m = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); my @w = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); # 国際標準時を定義 my $gmt = sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT", $w[$t[6]], $t[3], $m[$t[4]], $t[5]+1900, $t[2], $t[1], $t[0]); # URLエンコード my $cook; foreach (@cook) { s/(\W)/sprintf("%%%02X", unpack("C", $1))/eg; $cook .= "$_<>"; } # 格納 print "Set-Cookie: ASKA_BBS=$cook; expires=$gmt\n"; } #------------------------------------------------- # クッキー取得 #------------------------------------------------- sub get_cookie { # クッキーを取得 my $cook = $ENV{'HTTP_COOKIE'}; # 該当IDを取り出す my %cook; foreach ( split(/;/, $cook) ) { my ($key, $val) = split(/=/); $key =~ s/\s//g; $cook{$key} = $val; } # データをURLデコードして復元 my @cook; foreach ( split(/<>/, $cook{'ASKA_BBS'}) ) { s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("H2", $1)/eg; push(@cook,$_); } return @cook; } #------------------------------------------------- # crypt暗号 #------------------------------------------------- sub encrypt { my $in = shift; my @s = ('a'..'z', 'A'..'Z', '0'..'9', '.', '/'); srand; my $salt = $s[int(rand(@s))] . $s[int(rand(@s))]; crypt($in, $salt) || crypt ($in, '$1$' . $salt); } #------------------------------------------------- # crypt照合 #------------------------------------------------- sub decrypt { my ($in, $dec) = @_; my $salt = $dec =~ /^\$1\$(.*)\$/ && $1 || substr($dec, 0, 2); if (crypt($in, $salt) eq $dec || crypt($in, '$1$' . $salt) eq $dec) { return 1; } else { return 0; } } #------------------------------------------------- # メール送信 #------------------------------------------------- sub mail_to { # メールタイトル my $msub = "[$title : $no] $in{'sub'}"; $msub = &base64($msub); # 本文の改行・タグを復元 my $mcom = $in{'comment'}; $mcom =~ s/
/\n/g; $mcom =~ s/</</g; $mcom =~ s/>/>/g; $mcom =~ s/"/”/g; $mcom =~ s/&/&/g; my $mbody = "$titleに以下の投稿がありました。\n\n"; $mbody .= "Date : $date\n"; $mbody .= "Host : $host\n"; $mbody .= "Agent: $ENV{'HTTP_USER_AGENT'}\n\n"; $mbody .= "名前 : $in{'name'}\n"; $mbody .= "email: $in{'email'}\n"; $mbody .= "題名 : $in{'sub'}\n"; $mbody .= "参照 : $in{'url'}\n" if ($in{'url'}); $mbody .= "\n$mcom\n"; my $email; # メールアドレスがない場合は管理者アドレスに置き換え 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); } #------------------------------------------------- # 自動リンク #------------------------------------------------- sub auto_link { $_[0] =~ s/([^=^\"]|^)(https?\:[\w\.\~\-\/\?\&\=\@\;\#\:\%]+)/$1$2<\/a>/g; } #------------------------------------------------- # REFチェック #------------------------------------------------- sub refCheck { my $ref = $ENV{'HTTP_REFERER'}; $ref =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg; $baseUrl =~ s/(\W)/\\$1/g; if ($ref && $ref !~ /$baseUrl/i) { &error("不正なアクセスです"); } } #------------------------------------------------- # BASE64変換 #------------------------------------------------- # とほほのWWW入門で公開されているルーチンを参考にしました。 # http://www.tohoho-web.com/ sub base64 { my $sub = shift; &jcode::convert(\$sub, 'jis', 'sjis'); $sub =~ s/\x1b\x28\x42/\x1b\x28\x4a/g; "=?iso-2022-jp?B?" . &b64enc($sub) . "?="; } sub b64enc { my $ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; my ($x, $y, $z); $x = unpack("B*", $_[0]); for ( my $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 enter_form { &header; print <

パスワードを入力して下さい

EOM exit; } #------------------------------------------------- # メッセージ表示 #------------------------------------------------- sub message { my $msg = shift; &header; print <

$msg

EOM if ($in{'pass'} ne "") { print qq|\n|; print qq|\n|; } print < EOM exit; } #------------------------------------------------- # 禁止ワードチェック #------------------------------------------------- sub no_wd { my $flg; foreach ( split(/,/, $no_wd) ) { if (index("$in{'name'} $in{'comment'}",$_) >= 0) { $flg = 1; last; } } if ($flg) { &error("禁止ワードが含まれています"); } } #------------------------------------------------- # 日本語チェック #------------------------------------------------- sub jp_wd { if ($in{'comment'} !~ /[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]/) { &error("コメントに日本語が含まれていません"); } } #------------------------------------------------- # URL個数チェック #------------------------------------------------- sub urlnum { my $com = $in{'comment'}; my $num = ($com =~ s|(https?://)|$1|ig); if ($num > $urlnum) { &error("コメント中のURLアドレスは最大$urlnum個までです"); } } #------------------------------------------------- # チェックモード #------------------------------------------------- sub check { &header; print <Check Mode
    EOM # ログファイル if (-e $logfile) { print "
  • LOGパス:OK\n"; if (-r $logfile && -w $logfile) { print "
  • LOGパーミッション:OK\n"; } else { print "
  • LOGパーミッションが不正です。\n"; } } else { print "
  • LOGのパスが不正です:NG → $logfile\n"; } print < EOM exit; }