#!/usr/bin/perl #┌───────────────────────────────── #│ COM BOARD v5.1 (2004/02/06) #│ Copyright (c) KentWeb #│ webmaster@kent-web.com #│ http://www.kent-web.com/ #└───────────────────────────────── $ver = 'COM-BBS v5.1'; #┌───────────────────────────────── #│[ 注意事項 ] #│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した #│ いかなる損害に対して作者は一切の責任を負いません。 #│ 2. 設置に関する質問はサポート掲示板にお願いいたします。 #│ 直接メールによる質問は一切お受けいたしておりません。 #└───────────────────────────────── #------------------------------------------------- # 設定項目 #------------------------------------------------- # ライブラリ取込 require './jcode.pl'; # タイトル $title = "KARIYADO Elementary School"; # タイトルの色 $t_col = "#555555"; # タイトル文字サイズ $t_size = '24px'; # 本文文字サイズ $b_size = '13px'; # 文字のフォントタイプ $b_face = '"MS UI Gothic", "MS Pゴシック", Osaka'; # 背景色、文字色など $bg = "http://www.kariyado-e.ed.jp/cbs/enbo.jpg"; # 壁紙 (http://から記述) $bc = "#F0F0F0"; # 背景色 $te = "#000000"; # 文字色を指定 $li = "#0000FF"; # リンク色を指定(未訪問) $vl = "#800080"; # リンク色を指定(既訪問) # 戻り先のURL (index.htmlなど) $homepage = '../index.html'; # 最大記事数 $max = 300; # パスワード(英数字で指定) $pass = 'yadokari'; # 横割フレーム (0=no 1=yes) $yoko_frame = 0; # 投稿項目に「題名」を使う (0=no 1=yes) $subject = 1; # 投稿項目に「URL」を使用 (0=no 1=yes) $urlkey = 0; # スクリプトファイル名 $script = './cbs.cgi'; # ログファイル $logfile = './cbslog.cgi'; # 記事タイトルの色 $subcol = "#dd0000"; # 1ページ当たりの記事表示数 $p_log = 10; # ロックファイル機構 # → 0=no 1=symlink関数 2=mkdir関数 $lockkey = 0; # ロックファイル名 $lockfile = './lock/combbs.lock'; # URL自動リンク機能 (0=no 1=yes) $autolink = 1; # メール通知機能 # 0 : 通知しない # 1 : 通知する → 自分の投稿記事も通知する # 2 : 通知する → 自分の投稿記事は通知しない $mailing = 0; # メール通知先 (メール通知する時) $mailto = 'tamba@kamezaki-e.ed.jp'; # sendmailパス(メール通知する時) $sendmail = '/usr/lib/sendmail'; # タイトルに画像を使用する場合(画像ファイルをhttp://から記述) $t_img = ""; $t_w = 150; # 横幅 $t_h = 80; # 縦幅 # タグ広告挿入オプション # → の代わりに「広告タグ」を挿入する。 # → 広告タグ以外に、MIDIタグ や LimeCounter等のタグにも使用可能です。 $banner1 = ''; # 表示部上部に挿入 $banner2 = ''; # 表示部下部に挿入 # 記事の更新は method=POST 限定する場合(セキュリティ対策) # → 0=no 1=yes $postonly = 1; # 他サイトから投稿排除時に指定する場合(セキュリティ対策) # → 掲示板のURLをhttp://から書く $baseUrl = ''; # 投稿制限(セキュリティ対策) # 0 : しない # 1 : 同一IPアドレスからの投稿間隔を制限する # 2 : 全ての投稿間隔を制限する $regCtl = 1; # 制限投稿間隔(秒数) # → $regCtl での投稿間隔 $wait = 60; # ホスト取得方法 # 0 : gethostbyaddr関数を使わない # 1 : gethostbyaddr関数を使う $gethostbyaddr = 0; # アクセス制限(半角スペースで区切る) # → 拒否するホスト名又はIPアドレスを記述(アスタリスク可) # → 記述例 $deny = '*.anonymizer.com 211.154.120.*'; $deny = '60.6.60.*'; '61.199.77.*'; '60.40.51.*'; #------------------------------------------------- # 設定完了 #------------------------------------------------- # COM BOARDのメイン処理 $ret = &decode; &axsCheck; if (!$ret) { if ($yoko_frame) { &y_frame; } else { &t_frame; } } if ($mode eq "admin") { &admin; } elsif ($mode eq "form") { &form; } elsif ($mode eq "regist") { ®ist; } elsif ($mode eq "howto") { &howto; } elsif ($mode eq "find") { &find; } elsif ($mode eq "msg_del") { &msg_del; } &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); } if ($host eq "") { $host = $addr; } local($flag)=0; foreach ( split(/\s+/, $deny) ) { s/\./\\\./g; s/\*/\.\*/g; s/\?/\.\?/g; if ($host =~ /$_/i || $addr =~ /$_/i) { $flag=1; last; } } if ($flag) { &error("アクセスを許可されていません"); } } #------------------------------------------------- # HTMLのヘッダー #------------------------------------------------- sub header { if ($headflag) { return; } print "Content-type: text/html\n\n"; print < $title EOM $headflag=1; } #------------------------------------------------- # 縦割フレーム出力 #------------------------------------------------- sub t_frame { &header; print < <body>フレーム非対応のブラウザの方はご利用できません </body> EOM exit; } #------------------------------------------------- # 横割フレーム出力 #------------------------------------------------- sub y_frame { &header; print < <body>フレーム非対応のブラウザの方はご利用できません </body> EOM exit; } #------------------------------------------------- # 投稿フォーム #------------------------------------------------- sub form { # クッキー取得 local($cnam,$ceml,$curl,$cpwd) = &get_cookie; if (!$curl) { $curl = 'http://'; } # HTML出力 print "Content-type: text/html\n\n"; print < $title $body
EOM # フレーム if ($yoko_frame) { &yokoForm; } else { &tateForm; } print "\n"; exit; } #------------------------------------------------- # 縦フォーム #------------------------------------------------- sub tateForm { print <
E-mail

EOM # 題名を使用する場合 if ($subject) { print "Title

\n"; } print <Comment

Delete key
EOM if ($urlkey) { print "

参照先
"; print "\n"; } print < Automatic utterance elimination EOM } #------------------------------------------------- # 横フォーム #------------------------------------------------- sub yokoForm { print < EOM if ($subject) { print ""; print " EOM if ($urlkey) { print ""; print "\n"; } print <
お名前
E-mail
タイトル\n"; } print <
コメント
参照先
削除キー (英数字で8文字以内)
発言自動消去
EOM } #------------------------------------------------- # 記事表示部 #------------------------------------------------- sub html_log { local($i,$next,$back,$no,$date,$name,$eml,$sub,$com,$url); &header; print <top | | Retrieval | Managemen | EOM print "

\n"; print "$banner1

\n" if ($banner1 ne ""); # タイトル部 if ($t_img) { print "\"$title\"\n"; } else { print "$title\n"; } print "

\n"; $i=0; open(IN,"$logfile") || &error("Open Error: $logfile"); while () { $i++; if ($i < $page + 1) { next; } if ($i > $page + $p_log) { last; } ($no,$date,$nam,$eml,$sub,$com,$url) = split(/<>/); if ($eml) { $nam = "$nam"; } if ($autolink) { &auto_link($com); } if ($url) { $com .= "

$url"; } print "


[$no] "; if ($subject) { print "$sub "; print "Contributor :$nam Contribution day:$date

\n"; } else { print "$nam - $date

\n"; } print "
$com

\n"; } close(IN); print <
EOM $next = $page + $p_log; $back = $page - $p_log; print "\n"; if ($back >= 0) { print "\n"; } if ($next < $i) { print "\n"; } # 著作権表示(削除禁止) print <
\n"; print "\n"; print "
\n"; print "\n"; print "
No Delete key

$banner2

- Com Board -

EOM exit; } #------------------------------------------------- # 記事投稿 #------------------------------------------------- sub regist { local($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim,@file,@w); # 投稿チェック if ($postonly && !$post_flag) { &error("不正なアクセスです"); } if ($baseUrl) { &refCheck; } # 名前とコメントは必須 if ($in{'name'} eq "") { &error("名前が入力されていません"); } if ($in{'comment'} eq "") { &error("コメントが入力されていません"); } if ($in{'email'} && $in{'email'}!~ /[\w\.\-]+\@[\w\.\-]+\.[a-zA-Z]{2,6}$/) { &error("Eメールの入力内容が不正です"); } if ($in{'url'} eq "http://" || $in{'url'} eq "https://") { $in{'url'}=""; } # ロック処理 &lock if ($lockkey); # ログを開く open(IN,"$logfile") || &error("Open Error: $logfile"); @file = ; close(IN); # 重複投稿チェック local($no,$dat,$nam,$eml,$sub,$com,$url,$hos,$pw,$tim) = split(/<>/, $file[0]); if ($in{'name'} eq $nam && $in{'comment'} eq $com) { &error("二重投稿は禁止です"); } # 連続投稿チェック $time = time; $flag=0; if ($regCtl == 1) { if ($host eq $hos && $time - $tim < $wait) { $flag=1; } } elsif ($regCtl == 2) { if ($time - $tim < $wait) { $flag=1; } } if ($flag) { &error("現在投稿制限中です。もうしばらくたってから投稿をお願いします"); } # 記事Noを採番 $no++; # 削除キーを暗号化 if ($in{'pwd'} ne "") { $pwd = &encrypt($in{'pwd'}); } # 時間を取得 local($min,$hour,$mday,$mon,$year,$wday) = (localtime($time))[1..6]; @w = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); $date = sprintf("%04d/%02d/%02d(%s) %02d:%02d", $year+1900,$mon+1,$mday,$w[$wday],$hour,$min); # 更新 while ($max <= @file) { pop(@file); } unshift(@file,"$no<>$date<>$in{'name'}<>$in{'email'}<>$in{'sub'}<>$in{'comment'}<>$in{'url'}<>$host<>$pwd<>$time<>\n"); open(OUT,">$logfile") || &error("Write Error: $logfile"); print OUT @file; close(OUT); # ロック解除 &unlock if ($lockkey); # クッキーを発行 &set_cookie($in{'name'},$in{'email'},$in{'url'},$in{'pwd'}); # メール通知処理 if ($mailing == 1) { &mail_to; } elsif ($mailing == 2 && $in{'email'} ne $mailto) { &mail_to; } } #------------------------------------------------- # ユーザ記事削除 #------------------------------------------------- sub msg_del { local($num,$date,$name,$email,$sub,$com,$url,$host,$pw,$pw2,$flag,$match,@data); # 投稿チェック if ($postonly && !$post_flag) { &error("不正なアクセスです"); } if ($baseUrl) { &refCheck; } if ($in{'no'} eq "") { &error("記事NOが入力されていません"); } if ($in{'delkey'} eq "") { &error("削除キーが入力されていません"); } # ロック開始 if ($lockkey) { &lock; } # ログ読み込み $flag=0; @data=(); open(IN,"$logfile") || &error("Open Error: $logfile"); while () { ($num,$date,$name,$email,$sub,$com,$url,$host,$pw) = split(/<>/); if ($in{'no'} == $num) { if ($pw) { $pw2=$pw; $flag=1; } else { $flag=2; } next; } push(@data,$_); } close(IN); if ($flag == 0) { &error("記事Noが不正です"); } elsif ($flag == 2) { &error("削除キーが設定されていません"); } # パスワードを照合 $match = &decrypt($in{'delkey'}, $pw2); if ($match != 1) { &error("削除キーが違います"); } # ログを更新 open(OUT,">$logfile") || &error("Write Error: $logfile"); print OUT @data; close(OUT); # ロック解除 if ($lockkey) { &unlock; } # 記事表示画面に戻る &html_log; } #------------------------------------------------- # 留意事項 #------------------------------------------------- sub howto { &header; print <戻る]

掲示板の利用上の注意

  1. この掲示板はクッキー対応です。1度記事を投稿いただくと、 おなまえ、Eメール、URLの情報は2回目以降は自動入力されます。 (ただし利用者のブラウザがクッキー対応の場合)
  2. 投稿内容には、タグは一切使用できません。
  3. 記事を投稿する上での必須入力項目は「おなまえ」「メッセージ」です。
  4. 記事には、半角カナは一切使用しないで下さい。文字化けの原因となります。
  5. 記事の保持件数は最大 $max件です。それを超えると古い順に自動削除されます。
  6. 過去の投稿記事から「キーワード」によって簡易検索ができます。 トップメニューの「ワード検索」 のリンクをクリックすると検索モードとなります。
  7. 管理者が著しく不利益と判断する記事や他人を誹謗中傷する記事は予\告\なく削除することがあります。
EOM exit; } #------------------------------------------------- # ワード検索 #------------------------------------------------- sub find { &header; print <戻る]
  • 検索したいキーワードを入力し検索ボタンを押してください。
  • キーワードはスペースで区切って複数指定することができます。
    キーワード 条件 表\示
EOM # ワード検索の実行と結果表示 if ($in{'word'} ne "") { local($no,$ymd,$nam,$eml,$sub,$com,$url,$i, $wd,$flag,$next,$back,$enwd,@wd,@find); # 入力内容を整理 $in{'word'} =~ s/\x81\x40/ /g; @wd = split(/\s+/, $in{'word'}); # 検索 print "
\n"; open(IN,"$logfile") || &error("Open Error: $logfile"); while () { $flag=0; foreach $wd (@wd) { if (index($_,$wd) >= 0) { $flag=1; if ($in{'cond'} eq 'OR') { last; } } else { if ($in{'cond'} eq 'AND') { $flag=0; last; } } } if ($flag) { $i++; next if ($i < $page + 1); next if ($i > $page + $in{'view'}); push(@find,$_); } } close(IN); # 結果を表示 print "
$in{'word'} に関連する記事は$i件見つかりました。\n"; foreach (@find) { ($no,$ymd,$nam,$eml,$sub,$com,$url) = split(/<>/); if ($eml) { $nam="$nam"; } if ($url) { $com .= "

$url"; } # 結果を表示 print "


[$no] "; print "$sub " if ($subject); print "投稿者:$nam 投稿日:$ymd

\n"; print "
$com

\n"; } print "

\n"; $next = $page + $in{'view'}; $back = $page - $in{'view'}; $enwd = &url_enc($in{'word'}); if ($back >= 0) { print "[前の$in{'view'}件]\n"; } if ($next < $i) { print "[次の$in{'view'}件]\n"; } } print "\n"; exit; } #------------------------------------------------- # 管理モード #------------------------------------------------- sub admin { local($no,$date,$nam,$eml,$sub,$com,$url,$host); # 認証 if ($in{'pass'} eq "") { &enter; } elsif ($in{'pass'} ne $pass) { &error("パスワードが違います"); } # 削除 if ($in{'no'}) { local($f,$del,$no,@new); # ロック開始 &lock if ($lockkey); open(IN,"$logfile") || &error("Open Error: $logfile"); while () { ($no) = split(/<>/); $f=0; foreach $del ( split(/\0/, $in{'no'}) ) { if ($no == $del) { $f++; last; } } if (!$f) { push(@new,$_); } } close(IN); # 更新 open(OUT,">$logfile") || &error("Write Error: $logfile"); print OUT @new; close(OUT); # ロック解除 &unlock if ($lockkey); } &header; print <掲示板に戻る]
EOM # 記事展開 open(IN,"$logfile") || &error("Open Error: $logfile"); while () { ($no,$date,$nam,$eml,$sub,$com,$url,$host) = split(/<>/); if ($eml) { $nam = "$nam"; } $com =~ s/<[^>]*(>|$)//g; if (length($com) > 60) { $com = substr($com,0,58); $com .= "..."; } print "

[$no] "; print "$sub - " if ($subject); print "$nam <$date> 【$host】
$com\n"; } close(IN); print <
EOM exit; } #------------------------------------------------- # 入室画面 #------------------------------------------------- sub enter { &header; print < パスワードを入力して下さい
EOM exit; } #------------------------------------------------- # デコード処理 #------------------------------------------------- sub decode { local($buf, $key, $val); undef(%in); if ($ENV{'REQUEST_METHOD'} eq "POST") { $post_flag=1; if ($ENV{'CONTENT_LENGTH'} > 51200) { &error("投稿量が大きすぎます"); } read(STDIN, $buf, $ENV{'CONTENT_LENGTH'}); } else { $post_flag=0; $buf = $ENV{'QUERY_STRING'}; } foreach ( split(/&/, $buf) ) { ($key, $val) = split(/=/); $val =~ tr/+/ /; $val =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("H2", $1)/eg; # S-JISコード変換 &jcode'convert(*val, "sjis"); # タグ処理 $val =~ s/&/&/g; $val =~ s/"/"/g; $val =~ s//>/g; $val =~ s/\0//g; # 改行処理 if ($key eq "comment") { $val =~ s/\r\n/
/g; $val =~ s/\r/
/g; $val =~ s/\n/
/g; } else { $val =~ s/\r//g; $val =~ s/\n//g; } $in{$key} .= "\0" if (defined($in{$key})); $in{$key} .= $val; } if ($subject && $in{'sub'} eq "") { $in{'sub'} = "無題"; } $page = $in{'page'}; $mode = $in{'mode'}; # タイムゾーン設定 $ENV{'TZ'} = "JST-9"; if ($bg) { $bg = "background=\"$bg\""; } $body = "\n"; # 返り値 if ($buf) { 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,$mcom,$mbody); # タイトルを定義 $msub = "[$title : $no] $in{'sub'}"; # 本文の改行・タグを復元 $mcom = $in{'comment'}; $mcom =~ s/
/\n/g; $mcom =~ s/<//g; $mcom =~ s/"/"/g; $mcom =~ s/&/&/g; # 本文を定義 $mbody = <<"EOM"; 投稿日時:$date ホスト名:$host ブラウザ:$ENV{'HTTP_USER_AGENT'} おなまえ:$in{'name'} Eメール:$in{'email'} EOM if ($subject) { $mbody .= "タイトル:$in{'sub'}\n"; } $mbody .= "\n$mcom\n"; # 題名をBASE64化 $msub = &base64($msub); # メールアドレスがない場合は管理者アドレスに置き換え if ($in{'email'} eq "") { $email = $mailto; } else { $email = $in{'email'}; } # sendmail送信 open(MAIL,"| $sendmail -t") || &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 set_cookie { local(@cook) = @_; local($gmt, $cook, @t, @m, @w); @t = gmtime(time + 60*24*60*60); @m = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); @w = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); # 国際標準時を定義 $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エンコード foreach (@cook) { s/(\W)/sprintf("%%%02X", unpack("C", $1))/eg; $cook .= "$_<>"; } # 格納 print "Set-Cookie: COMBBS=$cook; expires=$gmt\n"; } #------------------------------------------------- # クッキー取得 #------------------------------------------------- sub get_cookie { local($key, $val, *cook); # クッキーを取得 $cook = $ENV{'HTTP_COOKIE'}; # 該当IDを取り出す foreach ( split(/;/, $cook) ) { ($key, $val) = split(/=/); $key =~ s/\s//g; $cook{$key} = $val; } # データをURLデコードして復元 foreach ( split(/<>/, $cook{'COMBBS'}) ) { s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("H2", $1)/eg; push(@cook,$_); } return (@cook); } #------------------------------------------------- # エラー処理 #------------------------------------------------- sub error { if ($lockflag) { &unlock; } &header; print <

ERROR !

$_[0]

EOM exit; } #------------------------------------------------- # 自動リンク処理 #------------------------------------------------- sub auto_link { $_[0] =~ s/([^=^\"]|^)(http\:[\w\.\~\-\/\?\&\+\=\:\@\%\;\#]+)/$1$2<\/a>/g; } #------------------------------------------------- # URLエンコード #------------------------------------------------- sub url_enc { local($_) = @_; s/(\W)/'%' . unpack('H2', $1)/eg; s/\s/+/g; $_; } #------------------------------------------------- # crypt暗号 #------------------------------------------------- sub encrypt { local($in) = @_; local($salt, $enc, @s); @s = ('a'..'z', 'A'..'Z', '0'..'9', '.', '/'); srand; $salt = $s[int(rand(@s))] . $s[int(rand(@s))]; $enc = crypt($in, $salt) || crypt ($in, '$1$' . $salt); $enc; } #------------------------------------------------- # crypt照合 #------------------------------------------------- sub decrypt { local($in, $dec) = @_; local $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); } } #------------------------------------------------- # REFチェック #------------------------------------------------- sub refCheck { local($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://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; } __END__