#!/usr/bin/perl #┌───────────────────────────────── #│ GATEWAY BOARD v3.2 (2002/08/01) #│ Copyright(C) Kent Web 2002 #│ webmaster@kent-web.com #│ http://www.kent-web.com/ #└───────────────────────────────── $ver = 'GATEWAY BOARD v3.2'; #┌───────────────────────────────── #│[ 注意事項 ] #│ 1. このスクリプトはフリーソフトです。このスクリプトを使用した #│ いかなる損害に対して作者は一切の責任を負いません。 #│ 2. 設置に関する質問はサポート掲示板にお願いいたします。 #│ 直接メールによる質問は一切お受けいたしておりません。 #└───────────────────────────────── # # [ ファイル構成例 ] # # public_html / index.html (トップページ) # | # +-- gwbbs / gwbbs.cgi [755] # | gwlog.cgi [666] # | jcode.pl [644] # | # +-- lock [777] / #============# # 基本設定 # #============# # 文字コード変換ライブラリ取り込み require './jcode.pl'; # タイトル名 $title = "メンバーBBS"; # タイトル文字の色 $t_color = "#006600"; # タイトル文字のタイプ $t_face = "MS Pゴシック"; # タイトル文字サイズ(スタイルシートで有効) $t_point = '13px'; # 本文文字サイズ(スタイルシートで有効) $b_point = '13px'; # 記事題名の色 $sub_color = "#DD0000"; # スクリプト名 $script = './gwbbs.cgi'; # ログファイル名 $logfile = './gwlog.cgi'; # 管理用パスワード $pass = '2269'; # 最大記事数(これを超える記事は古い順に削除されます) $max = 50; # 戻り先 $home = "../index.html"; # タイトル画像を使う場合 (http://から画像を指定) $ImgT = ""; $ImgW = 300; # 横幅サイズ $ImgH = 70; # 縦幅サイズ # bodyタグ $body = ''; # URLの自動リンク (0=no 1=yes) $autolink = 1; # 1ページあたりの記事表示件数 $p_log = 10; # ロックファイル機構 (0=no 1=symlink関数 2=mkdir関数) $lockkey = 0; # ロックファイル名 $lockfile = './lock/gwbbs.lock'; # メール通知機能 # 0 : 通知しない # 1 : 通知する → 自分の投稿記事も通知する # 2 : 通知する → 自分の投稿記事は通知しない $mailing = 0; # sendmailのパス(メール通知する場合) $mailprog = '/usr/lib/sendmail'; # メール通知先アドレス(メール通知する場合) $mailto = 'xxx@xxx.xxx'; # タグ広告挿入オプション (FreeWebなど) # → の代わりに「広告タグ」を挿入する。 # → 広告タグ以外に、MIDIタグ や LimeCounter等のタグにも使用可能です。 $banner1 = ''; # 表示部上部に挿入 $banner2 = ''; # 表示部下部に挿入 # 記事の更新は method=POST 限定 (0=no 1=yes) # (セキュリティ対策) $postonly = 1; # 同一IPアドレスからの連続投稿時間(秒数) # --> 連続投稿などの荒らし対策 # --> 値を 0 にするとこの機能は無効になります $wait = 60; # ID/PASS設定(上下は必ずペアで指定) @ID = ('taro', 'jiro', 'sabu'); @PW = ('0123', '1234', '5678'); #============# # 設定完了 # #============# &decode; if (!$buffer) { &enter; } elsif ($mode eq 'pass_chk') { &pass_chk("in"); } elsif ($mode eq 'admin') { &admin; } else { &pass_chk; } if ($mode eq 'regist') { ®ist; } if ($mode eq 'find') { &find; } if ($mode eq 'howto') { &howto; } if ($mode eq 'usrdel') { &usrdel; } &html; #----------------------# # パスワード入室画面 # #----------------------# sub enter { &get_cookie("pwd"); &header; print <<"EOM"; $body [戻る]

$title

- USER ID と PASSWORD を入力して下さい -

USER ID
PASSWORD



- GateWayBoard -
EOM exit; } #------------------# # 認証エラー処理 # #------------------# sub pass_err { &header; print <<"EOM"; $body


認証されませんでした

EOM exit; } #-----------------------# # ID/PASSチェック処理 # #-----------------------# sub pass_chk { # フォーム入力のチェック if ($in{'id'} eq "" || $in{'pw'} eq "") { &error("IDまたはパスワードが入力されていません"); } # ID/PASSをチェックする local($flag)=0; foreach (0 .. $#ID) { if ($in{'id'} eq $ID[$_] && $in{'pw'} eq $PW[$_]) { $flag=1; last; } } if (!$flag) { &pass_err; } # フレームを出力 if ($_[0] eq 'in') { # ID/PASS情報をクッキー格納 &set_cookie("pwd"); # クッキーを取得 &get_cookie; } } #----------------# # 記事表示処理 # #----------------# sub html { # クッキー情報を取得 &get_cookie; &header; print <<"EOM";
$banner1

EOM # タイトル if ($ImgT) { print "\"$title\"\n"; } else { print "$title\n"; } # 返信 if ($in{'res'} > 0) { open(IN,"$logfile") || &error("Open Error : $logfile"); while () { @r = split(/<>/); last if ($in{'res'} == $r[0]); } close(IN); # 引用符 $r[4] =~ s/^Re://g; $r[4] = "Re:[$r[0]] $r[4]"; while ($r[5] =~ /
$/) { $r[5] =~ s/
$//g; } $r[5] = "> $r[5]"; $r[5] =~ s/
/\r> /g; } print <<"EOM";


おなまえ
Eメール
題  名
メッセージ
URL
削除キー (記事削除用。英数字で8文字以内)
クッキー情報を保存
EOM # ページ区切り処理 $start = $page + 1; $end = $page + $p_log; $i=0; open(IN,"$logfile") || &error("Open Error : $logfile"); while () { $i++; if ($i < $start) { next; } if ($i > $end) { last; } ($no,$date,$name,$mail,$sub,$com,$url,$host,$pwd) = split(/<>/); if ($mail) { $name = "$name"; } if ($url) { $url = "http://$url"; } if ($autolink) { &auto_link($com); } print "

"; print "\n"; print "
[$no] "; print "$sub "; print "投稿者:$name 投稿日:$date
\n"; print "\n"; print "\n"; print "\n"; print "


\n"; print "
$com\n"; if ($url) { print "

$url\n"; } print "

\n"; } close(IN); print "


\n"; $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 "
\n"; print "\n"; print "\n"; print "\n"; print "
\n"; print "\n"; print "\n"; print "
\n"; print "\n"; print "\n"; print "\n"; print "記事No \n"; print "削除キー \n"; print "

\n"; # 著作権表示(削除不可) print "
$banner2

\n"; print "- GateWayBoard -\n"; print "

\n\n\n"; exit; } #----------------# # 書きこみ処理 # #----------------# sub regist { local($no2,$date2,$name,$mail,$sub,$com,$url,$host2,$pw2,$time2); # POST限定 if ($postonly && !$post_flag) { &error("不正なアクセスです"); } # フォーム内容をチェック if ($in{'name'} eq "") { &error("名前が入力されていません"); } if ($in{'comment'} eq "") { &error("コメントが入力されていません"); } # ロック処理 if ($lockkey) { &lock; } open(IN,"$logfile") || &error("Open Error : $logfile"); @lines = ; close(IN); # ホスト名を取得 &get_host; # 重複投稿チェック ($no2,$date2,$name,$mail,$sub,$com,$url,$host2,$pw2,$time2) = split(/<>/, $lines[0]); if ($in{'name'} eq $name && $in{'comment'} eq $com) { &error("二重投稿は禁止です"); } # 同一ホストの連続投稿チェック if ($host eq $host2 && $wait > $time - $time2) { &error("連続投稿はもうしばらく時間をおいて下さい"); } # 記事Noを採番 $no = $no2 + 1; # 削除キーを暗号化 if ($in{'pwd'} ne "") { $pwd = &encrypt($in{'pwd'}); } # 最大記事数処理 while ($max <= @lines) { pop(@lines); } # 更新 unshift(@lines,"$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 @lines; close(OUT); # ロック解除 if ($lockkey) { &unlock; } # クッキーを記憶 if ($in{'cook'} eq 'on') { &set_cookie; } # メール通知処理 if ($mailing == 1) { &mail_to; } elsif ($mailing == 2 && $in{'email'} ne "$mailto") { &mail_to; } # 完了メッセージ &header; print <<"EOM";

登録は正常に受け付けられました

EOM exit; } #--------------# # ワード検索 # #--------------# sub find { &header; print <<"EOM";
ワード検索

\n"; # ワード検索の実行と結果表示 if ($in{'word'} ne "") { # 入力内容を整理 $in{'word'} =~ s/ / /g; @pairs = split(/\s+/, $in{'word'}); # 検索 open(IN,"$logfile") || &error("Open Error : $logfile"); while () { $flag=0; foreach $pair (@pairs) { if (index($_,$pair) >= 0) { $flag=1; if ($in{'cond'} eq 'OR') { last; } } else { if ($in{'cond'} eq 'AND') { $flag=0; last; } } } if ($flag) { push(@new,$_); } } close(IN); # 検索終了 $count = @new; print "
検索結果:$count件\n"; if ($in{'page'} eq '') { $in{'page'} = 0; } $end_data = @new - 1; $page_end = $in{'page'} + $in{'view'} - 1; if ($page_end >= $end_data) { $page_end = $end_data; } foreach ($in{'page'} .. $page_end) { # 結果を表示 ($no,$date,$name,$mail,$sub,$com,$url,$host,$pwd) = split(/<>/, $new[$_]); if ($mail) { $name = "$name"; } print "

[$no] $sub "; print "投稿者:$name 投稿日:$date

$com\n"; if ($url) { print "

$url\n"; } print "

\n"; } close(IN); print "


\n"; $next_line = $page_end + 1; $back_line = $in{'page'} - $in{'view'}; print "\n"; if ($back_line >= 0) { print "\n"; } if ($page_end ne $end_data) { print "\n"; } print "
\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "
\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "
\n"; } print "\n\n"; exit; } #--------------# # 管理モード # #--------------# sub admin { if ($in{'pass'} eq "") { &header; print "

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

\n"; print "
\n"; print "\n"; print "\n"; print "\n"; print ""; print "
\n"; print "\n"; exit; } if ($in{'pass'} ne $pass) { &error("パスワードが違います"); } &header; print "
\n"; if ($id && $pw) { print "
\n"; print "\n"; print "\n"; print "\n"; } else { print "\n"; print "\n"; } print "
\n"; print "
\n"; print "管理モード\n"; print "
\n"; # 削除処理 if ($DEL[0]) { # POST限定 if ($postonly && !$post_flag) { &error("不正なアクセスです"); } # ロック処理 if ($lockkey) { &lock; } # 削除情報をマッチングし更新 open(IN,"$logfile") || &error("Open Error : $logfile"); @lines = ; close(IN); @new=(); foreach (@lines) { $flag=0; ($no,$date) = split(/<>/); foreach $del (@DEL) { if ($no eq "$del") { $flag=1; last; } } if ($flag == 0) { push(@new,$_); } } open(OUT,">$logfile") || &error("Write Error : $logfile"); print OUT @new; close(OUT); # ロック解除 if ($lockkey) { &unlock; } } # 削除画面を表示 print "
\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "

削除したい記事のチェックボックスにチェックを入れ、削除ボタンを押して下さい\n"; print "

\n"; print ""; print "\n"; open(IN,"$logfile") || &error("Open Error : $logfile"); while () { ($no,$date,$name,$mail,$sub,$com,$url,$host,$pwd) = split(/<>/); if ($mail) { $name="$name"; } $com =~ s/
//ig; $com =~ s//>/g; if (length($com) > 60) { $com = substr($com,0,58); $com .= "..."; } print "
"; print ""; print "\n"; } close(IN); print "
削除記事No投稿日題名投稿者コメントホスト名
$no$date$sub$name$com$host
\n"; print "

"; print "

\n"; print "
\n\n\n"; exit; } #--------------# # 使い方表示 # #--------------# sub howto { &header; print <<"EOM";
掲示板の使い方

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

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

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

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

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

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

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

  8. 管理者が著しく不利益と判断する記事や他人を誹謗中傷する記事は予\告なく削除することがあります。
EOM exit; } #------------------# # ユーザ記事削除 # #------------------# sub usrdel { # POST限定 if ($postonly && !$post_flag) { &error("不正なアクセスです"); } if ($in{'no'} eq '' || $in{'pwd'} eq '') { &error("削除Noまたは削除キーが入力モレです"); } # ロック処理 if ($lockkey) { &lock; } open(IN,"$logfile") || &error("Open Error : $logfile"); @lines = ; close(IN); $flag=0; @new=(); foreach (@lines) { ($no,$date,$name,$mail,$sub,$com,$url,$host,$pwd) = split(/<>/); if ($in{'no'} == $no) { $flag=1; $PWD=$pwd; } else { push(@new,$_); } } if ($flag == 0) { &error("該当記事が見当たりません"); } if ($PWD eq '') { &error("該当記事には削除キーが設定されていません"); } # 削除キーを照合 $match = &decrypt("$in{'pwd'}","$PWD"); if ($match ne 'yes') { &error("削除キーが違います"); } # ログを更新 open(OUT,">$logfile") || &error("Write Error : $logfile"); print OUT @new; close(OUT); # ロック解除 if ($lockkey) { &unlock; } } #----------------# # デコード処理 # #----------------# sub decode { local($name, $value, @pairs); if ($ENV{'REQUEST_METHOD'} eq "POST") { $post_flag=1; if ($ENV{'CONTENT_LENGTH'} > 51200) { &error("投稿量が大きすぎます"); } read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } else { $post_flag=0; $buffer = $ENV{'QUERY_STRING'}; } @pairs = split(/&/, $buffer); foreach (@pairs) { ($name, $value) = split(/=/); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # S-JISコード変換 &jcode'convert(*value,'sjis'); # タグ処理 $value =~ s/&/&/g; $value =~ s/"/"/g; $value =~ s//>/g; # 改行処理 if ($name eq "comment") { $value =~ s/\r\n/
/g; $value =~ s/\r/
/g; $value =~ s/\n/
/g; } else { $value =~ s/\r//g; $value =~ s/\n//g; } # 削除情報 if ($name eq 'del') { push(@DEL,$value); } $in{$name} = $value; } $in{'url'} =~ s/^http\:\/\///; if ($in{'sub'} eq "") { $in{'sub'} = "無題"; } $page = $in{'page'}; $mode = $in{'mode'}; $id = $in{'id'}; $pw = $in{'pw'}; # 日時の取得 $ENV{'TZ'} = "JST-9"; $time = time; ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($time); # 日時のフォーマット @week = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); $date = sprintf("%04d/%02d/%02d(%s) %02d:%02d", $year+1900,$mon+1,$mday,$week[$wday],$hour,$min); } #--------------# # HTMLヘッダ # #--------------# sub header { $head_flag = 1; print "Content-type: text/html\n\n"; print <<"EOM"; $title $body EOM } #--------------# # エラー処理 # #--------------# sub error { if ($lockflag) { &unlock; } &header if (!$head_flag); print "

ERROR !

\n"; print "$_[0]\n"; print "


\n\n\n"; exit; } #------------------# # クッキーの発行 # #------------------# sub set_cookie { local($gmt, @mon); # クッキーIDを定義 if ($_[0] eq "pwd") { $cook_id = "gw_pwd"; } else { $cook_id = "gw_bbs"; } local($sec,$min,$hour,$mday,$mon,$year,$wday) = gmtime(time + 60*24*60*60); @mon = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); $gmt = sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT", $week[$wday],$mday,$mon[$mon],$year+1900,$hour,$min,$sec); if ($_[0] eq "pwd" && $in{'chk'} eq 'on') { $cook = "id<>$in{'id'}<>pw<>$in{'pw'}"; } elsif ($_[0] eq "pwd" && $in{'chk'} ne 'on') { $cook = 'id:<>pw:'; } else { $cook = "name<>$in{'name'}<>email<>$in{'email'}<>url<>$in{'url'}<>pwd<>$in{'pwd'}"; } print "Set-Cookie: $cook_id=$cook; expires=$gmt\n"; } #------------------# # クッキーを取得 # #------------------# sub get_cookie { local($key, $val, @cook); # クッキーIDを定義 if ($_[0] eq "pwd") { $cook_id = "gw_pwd"; } else { $cook_id = "gw_bbs"; } @cook = split(/;/, $ENV{'HTTP_COOKIE'}); foreach (@cook) { ($key, $val) = split(/=/); $key =~ s/\s//g; $tmp{$key} = $val; } %ck = split(/<>/, $tmp{$cook_id}); } #----------------# # ホスト名取得 # #----------------# sub get_host { $host = $ENV{'REMOTE_HOST'}; $addr = $ENV{'REMOTE_ADDR'}; if ($host eq "" || $host eq $addr) { $host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2) || $addr; } } #----------------------# # パスワード暗号処理 # #----------------------# sub encrypt { local($inpw) = $_[0]; local(@SALT, $salt, $encrypt); @SALT = ('a'..'z', 'A'..'Z', '0'..'9', '.', '/'); srand; $salt = $SALT[int(rand(@SALT))] . $SALT[int(rand(@SALT))]; $encrypt = crypt($inpw, $salt) || crypt ($inpw, '$1$' . $salt); return $encrypt; } #----------------------# # パスワード照合処理 # #----------------------# sub decrypt { local($inpw, $logpw) = @_; local($salt, $key, $check); $salt = $logpw =~ /^\$1\$(.*)\$/ && $1 || substr($logpw, 0, 2); $check = "no"; if (crypt($inpw, $salt) eq $logpw || crypt($inpw, '$1$' . $salt) eq $logpw) { $check = "yes"; } return $check; } #--------------# # ロック処理 # #--------------# sub lock { # 1分以上古いロックは削除する if (-e $lockfile) { local($mtime) = (stat($lockfile))[9]; if ($mtime < time - 60) { &unlock; } } local($retry) = 5; # 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 { # メールプログラムのパスチェック unless (-e $mailprog) { &error("メールプログラムのパスが不正です"); } # メールタイトルを定義 $MailSub = "[$title : $no] $in{'sub'}"; # 本文の改行を復元 $m_com = $in{'comment'}; $m_com =~ s/
/\n/g; $MailBody = <<"EOM"; $titleに以下の投稿がありました。 投稿日時:$date ホスト名:$host ブラウザ:$ENV{'HTTP_USER_AGENT'} おなまえ:$in{'name'} Eメール:$in{'email'} タイトル:$in{'sub'} URL :http://$in{'url'} コメント: $m_com EOM # JISコード変換 &jcode'convert(*MailSub,'jis'); &jcode'convert(*MailBody,'jis'); if ($in{'email'} eq "") { $email2 = $mailto; } else { $email2 = $in{'email'}; } # sendmail送信 open(MAIL,"| $mailprog -t") || &error("メール送信に失敗しました"); print MAIL "To: $mailto\n"; print MAIL "From: $email2\n"; print MAIL "Subject: $MailSub\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"; print MAIL "--------------------------------------------------------\n"; print MAIL "$MailBody\n"; print MAIL "--------------------------------------------------------\n"; close(MAIL); } #--------------# # 自動リンク # #--------------# sub auto_link { $_[0] =~ s/([^=^\"]|^)(http\:[\w\.\~\-\/\?\&\=\@\;\#\:\%]+)/$1$2<\/a>/g; }