#!/usr/local/bin/perl #================================================================== # # Lounge: Version 1.30 # # Copyright (C) 1997, 1999 とほほ s-hasei@mtg.biglobe.ne.jp # http://www2e.biglobe.ne.jp/~s-hasei/ # # 個人、法人を問わず、自由に改造・使用・再配布可能です。 # # # For Hobby World Y.Kitami # Version 1.0.2 1999/2/14 # 1.0.3 1999/2/24 Modify # 1.0.4 1999/2/25 Modify # 1.0.5 1999/4/8 Modify #================================================================== # # 履歴 # 1997.06.15 初版 # 1997.07.02 &の処理を修正 # 1997.08.03 管理画面、削除機能、メール送信機能を追加 # 1997.08.10 表示の文字化け # 1997.08.11 逆順にソートするようにした # 1997.09.28 公開用に大幅改正 # 1997.09.30 [送]機能と[削]機能が動かないバグを修正 # 1997.10.19 管理者用パスワードのチェックミスを修正 # 1997.10.19 管理者へのメール送信機能のバグを修正 # 1998.01.11 1月のメッセージが12月より前に表示されるようにした # 1998.02.22 管理者モードの削除機能でTARGET=outを指定 # 1998.02.22 管理者モードのFrom欄にはファイル名を表示するようにした # 1998.02.22 http://ではじまる部分をリンク張るようにした # 1998.02.22 本文中のFrom:やDate:を評価してしまうバグを修正 # 1998.03.01 古いものをアーカイブするようにした # 1998.03.02 <や>処理のミスを修正、ソートに時間も考慮するようにした # 1998.03.11 一括表示機能を試験的にサポート # 1998.03.15 「ー」を検索するとエラーになるバグ修正 # 1998.03.29 にサブジェクト名を表示するようにした # 1998.03.29 空メッセージは記録しないようにした # 1998.04.12 x-sjisを指定しないようにした # 1998.05.24 文字化けするというバグを修正 # 1998.05.24 空メッセージの際のエラーメッセージが表示されないバグを修正 # 1998.05.24 管理者モードでタイトルがずれるバグを修正 # 1998.09.27 Ver1.25 ページの頭の方に[戻る]リンクを追加した # 1998.11.08 Ver1.26 EUCを指定してもsjisで書き込まれるバグ修正 # 1998.11.15 Ver1.27 セキュリティ上の問題を修正 # 1999.02.07 Ver1.30 n日分一括表示機能/SIGPIPE対処/50件表示/2000年対応 # # # 呼び出し形式 # # (1) wwwlng.cgi ... 最初の表示 # (2) wwwlng.cgi?dir+file ... 過去ログの表示 # (2) wwwlng.cgi?create ... 新規サブジェクト作成 # (3) wwwlng.cgi?print+file ... 指定サブジェクト表示 # (4) wwwlng.cgi?write+file ... 指定サブジェクトへの追加書き込み # (5) wwwlng.cgi?manage+passwd ... 最初の表示(管理機能付き) # (6) wwwlng.cgi?delete+passwd+file ... 削除 # (7) wwwlng.cgi?send+passwd+file ... メール送信 # (8) wwwlng.cgi?test ... 自己テスト # (9) wwwlng.cgi?search+keyword ... 検索 # (10) wwwlng.cgi?list+n ... 指定サブジェクト表示 # # # 変更可能パラメータ # $return_url = '../index.htm'; # [戻る]ボタンの戻り先 $password = ''; # 管理者用パスワード $mailto = ''; # メールアドレス $sendmail = '/usr/lib/sendmail'; # メール送信コマンド $nkf = '/usr/local/bin/nkf'; # 漢字コード変換コマンド $file_lock_flag = 1; # ファイルロックを行うか $lock_file = "lock/wwwlng.loc"; # ロックファイル $do_search = 1; # 検索機能を有効にする # # 初期化処理 # $ENV{'TZ'} = "JST-9"; @g_wdays = ( "日", "月", "火", "水", "木", "金", "土" ); $g_dirname = "wwwlng"; # 対象ディレクトリ名 $g_filename = ""; # 対象ファイル名 $SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit"; # # 検索用パラメータ # $kanji_code = "sjis"; # "sjis" または "euc" $target_dir = "wwwlng"; # 検索対象ディレクトリ $sufix{".htm"} = 1; # 拡張子 .htm を検索する $sufix{".html"} = 1; # 拡張子 .html を検索する $do_logging = 1; # 検索キーワードのロギング $do_progress = 0; # 検索状況表示 $| = 1; # 出力をバッファリングしないようにする require "jcode.pl"; # 漢字コード変換ライブラリ読み込み # # メインルーチン # { &readParam(); # サブジェクト一覧表示 if ($#ARGV == -1) { &printHeader(); &printSubjectsList("wwwlng", "normal"); } # サブジェクト一覧表示(過去の履歴) elsif ($ARGV[0] eq "dir") { &printHeader(); &printSubjectsList("$g_dirname/$ARGV[1]", "hist"); } # サブジェクト新規作成 elsif ($ARGV[0] eq "create") { &fileLock(); &createSubject() || goto done; &writeMessage() || goto done; &fileUnlock(); &printSubject(); } # サブジェクト表示 elsif ($ARGV[0] eq "print") { $g_filename = $ARGV[1]; if ($g_filename =~ /(^\/|\.\.)/) { &printHeader(); print "他のフォルダのファイルを参照することはできません。\n"; goto done; } &printSubject(); } # 過去のn日分表示 elsif ($ARGV[0] eq "list") { &printHeader(); $days = $ARGV[1]; &printList(); } # サブジェクトに発言を追加 elsif ($ARGV[0] eq "write") { $g_filename = $ARGV[1]; if ($g_filename =~ /(^\/|\.\.)/) { &printHeader(); print "他のフォルダのファイルを参照することはできません。\n"; goto done; } &fileLock(); &writeMessage() || goto done; &fileUnlock(); &printSubject(); } # サブジェクト一覧表示(管理者用) elsif ($ARGV[0] eq "manage") { &printHeader(); &passwdCheck($ARGV[1]) || goto done; &printSubjectsList($g_dirname, "manage"); } # サブジェクト削除(管理者用) elsif ($ARGV[0] eq "delete") { &printHeader(); &passwdCheck($ARGV[1]) || goto done; &deleteSubject($ARGV[2]); } # サブジェクトメール送信(管理者用) elsif ($ARGV[0] eq "send") { &printHeader(); &passwdCheck($ARGV[1]) || goto done; &sendMail($ARGV[2]); } # 自己診断機能 elsif ($ARGV[0] eq "test") { &printHeader(); &localTest(); } # 検索 elsif ($ARGV[0] eq "search") { &printHeader(); &searchKeyword(); } done: &printFooter(); } # # ヘッダを書き出す # sub printHeader { print "Content-type: text/html\n"; print "\n"; print "<HTML>\n"; print "<HEAD>\n"; print "<TITLE>Low Rider's ラウンジ\n"; print "\n"; print "\n"; } # # フッタを書き出す # sub printFooter { print "
\n"; print "\n"; print "\n"; } # # ラウンジファイルにメッセージを書き込む # sub createSubject { # # 入力パラメータのチェック # if (($FORM{'SUBJECT'} eq "") || ($FORM{'MESSAGE'} eq "")) { print "Content-type: text/html\n"; print "\n"; print "新規サブジェクトを追加するには最低限、"; print "題名内容を入力してください。\n"; print "
\n"; print "\n"; print "
\n"; return(undef); } if ($FORM{'FROM'} eq "") { $FORM{'FROM'} = "匿名希望"; } # # 使用されていないファイル名を探す # ($sec, $min, $hour, $mday, $mon, $year) = localtime(); $year %= 100; for ($i = 0; $i < 10000; $i++) { $g_filename = sprintf("%02d%02d%04d.txt", $year % 100, $mon+1, $i); $tmp = sprintf("lng%02d%02d", $year % 100, $mon + 1); if (!(-f "$g_dirname/$g_filename") && !(-f "$g_dirname/$tmp/$g_filename")) { last; } } if ($i == 10000) { print "今月はもうこれ以上サブジェクトを作成できません。\n"; return(undef); } # # サブジェクトを書き込む # open(OUT, ">> $g_dirname/$g_filename"); print OUT "Subject: $FORM{'SUBJECT'}\n"; if (($FORM{'REPLY'} eq "on") && ($FORM{'EMAIL'} =~ /.*@.*/)) { print OUT "Reply-To: $FORM{'EMAIL'}\n"; } close(OUT); return(1); } # # ラウンジファイルの一覧を表示する # &printSubjectsList(dir, listmode) # dir: ディレクトリ("wwwlng" or "wwwlng/lng9812") # listmode: "normal"=通常 "hist"=過去履歴 "manage"=管理者 # sub printSubjectsList { # 引数を得る $dirname = $_[0]; $listmode = $_[1]; # 今月($mon1)と先月($mon2)を得る ($sec, $min, $hour, $mday, $mon, $year) = localtime(); $mon1 = sprintf("%04d/%02d", $year + 1900, $mon + 1); if ($mon == 0) { $mon = 12; $year--; } $mon2 = sprintf("%04d/%02d", $year + 1900, $mon); # ディレクトリ内のファイルの一覧を得る opendir(DIR, "$dirname"); @files = readdir(DIR); closedir(DIR); # それぞれのファイルについて foreach $file (@files) { if (-d "$dirname/$file") { if (($file ne ".") && ($file ne "..")) { push(@dirs, "
  • $file
    \n"); } next; } unless (open(IN, "$dirname/$file")) { next; } $title = ; $title =~ s/^Subject: //; $count = 0; $solved = "
    "; while () { if ($_ eq "========================================\n") { $flag = 1; } if ($_ eq "\n") { $flag = 0; } if ($flag == 0) { next; } if ($_ =~ /^From: /) { $from = $_; $from =~ s/^From: //; $count++; } if ($_ =~ /^Solved:/) { $solved = "済"; } if ($_ =~ /^Date:/) { $date = substr($_, 6, 10); $time = substr($_, 21, 8); } } close(IN); # 先先月以前のサブジェクトであれば過去のフォルダに移す if (($listmode ne "hist") && !($date =~ $mon1) && !($date =~ $mon2)) { $dir = "$g_dirname/lng" . substr($date, 2, 2) . substr($date, 5, 2); mkdir($dir, 0777); open(IN, "$g_dirname/$file"); open(OUT, "> $dir/$file"); while () { print OUT; } close(IN); close(OUT); if ((-s "$g_dirname/$file") == (-s "$dir/$file")) { unlink("$g_dirname/$file"); } next; } # 1行分のエントリを作成する if ($listmode eq "manage") { $item = "" . "$date" . "" . "$file" . "$solved" . "" . "" . "$title ($count件)" . "\n"; } else { if ($listmode eq "hist") { $file = substr("$dirname/$file", 7); } $item = "" . "$date" . "" . "$from" . "$solved" . "$title ($count件)" . "\n"; } push(@items, $item); } # ソートする @items = sort { $b cmp $a; } @items; @dirs = sort { $b cmp $a; } @dirs; # 50件以上古いものを過去の履歴にほうり込む # if ($listmode ne "hist") { # @logs = splice(@items, 50); # foreach $_ (@logs) { # $_ =~ /([0-9][0-9]\/[0-9][0-9]\/[0-9][0-9])/; # $date = $1; # $_ =~ /print\+([0-9]*.txt)/i; # $file = $1; # $dir = "$g_dirname/lng" . substr($date, 0, 2) . substr($date, 3, 2); # mkdir($dir, 0777); # open(IN, "$g_dirname/$file"); # open(OUT, "> $dir/$file"); # while () { # print OUT; # } # close(IN); # close(OUT); # if ((-s "$g_dirname/$file") == (-s "$dir/$file")) { # unlink("$g_dirname/$file"); # } # } # } # 書き出す print "\n"; print "

    Low Rider's ラウンジ

    \n"; if ($listmode ne "hist") { print "Low Rider とDYNAのラウンジです。誰でも自由に書き込んでください。\n"; print "テクニカル、ノンテクニカル、雑談、何でもOKです。\n"; print "気になったテーマを話題にして話し合うもよし。質問するもよし。\n"; print "そふていら〜、ろうきんなども歓迎します。\n"; print "

    \n"; if ($return_url ne "") { print "[戻る]\n"; } print "[新規質問]\n"; print "[過去の履歴]\n"; } print "


    \n"; print "質問の一覧\n"; print "
    \n"; print "◎ 単語 を "; print "\n"; print "
    ◎ ※欄にチェックした項目を \n"; print "
    ◎ 過去 "; print "日分の項目を "; print "\n"; print "

    \n"; print "\n"; print "\n"; if ($listmode eq "manage") { print ""; print ""; print ""; print ""; print ""; print ""; } else { print ""; print ""; print ""; print ""; } print "\n"; print @items; print "
    最終
    更新日
    ファイル名


    題名最終
    更新日
    最終
    発言者

    題名
    \n"; print "

    \n"; print "

    \n"; if ($listmode eq "hist") { return; } print "


    \n"; print ""; print "新しい質問を書き込む"; print "\n"; print "
      \n"; print "
    • 誰でも自由に質問/投稿できます。\n"; print "
    • 質問の場合は質問形式で、それ以外は自由形式\n"; print "
    • 名前、メールアドレス、URLは匿名(空欄)でもかまいません。\n"; print "
    • 終了したらお礼を書き込んで[解決]をチェックしてください。\n"; print "
    • HTMLのタグは使えません。そのまま表\示されます。\n"; print "
    • 再表\示を行うと発言が重複する事があります。注意してください。"; print "
    \n"; print "
    \n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "
    名 前:"; print "
    メール:"; print "
    URL:"; print "
    題 名:"; print "
    内 容:
    \n"; print "
    \n"; print ""; print "回答をメールで受け取る(メールアドレス入力必須)\n"; print "

    \n"; print "\n"; print "

    \n"; print "
    \n"; print "過去の履歴"; print "
      \n"; print @dirs; print "
    \n"; } # # 発言を追加する # sub writeMessage { # # 空発言は無視する # if ($FORM{'MESSAGE'} eq "") { return(1); } # # 名前無記入は「匿名希望」とする # if ($FORM{'FROM'} eq "") { $FORM{'FROM'} = "匿名希望"; } # # ラウンジファイルに発言を追加する # open(OUT, ">> $g_dirname/$g_filename"); print OUT "========================================\n"; print OUT "From: $FORM{'FROM'}\n"; if ($FORM{'EMAIL'} ne "") { print OUT "E-Mail: $FORM{'EMAIL'}\n"; } if ($FORM{'HPAGE'} ne "") { print OUT "HomePage: $FORM{'HPAGE'}\n"; } ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(); $date = sprintf("%04d/%02d/%02d(%s) %02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $g_wdays[$wday], $hour, $min, $sec); print OUT "Date: $date\n"; if ($FORM{'SOLVED'} eq "on") { print OUT "Solved: $FORM{'SOLVED'}\n"; } print OUT "\n"; print OUT "$FORM{'MESSAGE'}\n"; close(OUT); # # 質問者にメールで知らせる # $mailto = ""; open(IN, "$g_dirname/$g_filename"); while () { if (/^Reply-To:/) { chop($_); $mailto = $_; $mailto =~ s/^Reply-To: //; } if (/^========/) { last; } } if ($mailto ne "") { seek(IN, 0, 0); open(OUT, "| $nkf -j | $sendmail $mailto"); print OUT "To: $mailto\n"; print OUT "Subject: Tohoho Lounge\n"; print OUT "\n"; while () { $_ =~ s/<//g; $_ =~ s/&/&/g; print OUT; } close(OUT); } close(IN); return(true); } # # 過去n日分を表示する # sub printList { $nowtime = time(); $limittime = $nowtime - ($days * 3600 * 24); opendir(DIR, "wwwlng"); @filelist = readdir(DIR); closedir(DIR); foreach $file (@filelist) { if ($file !~ /.txt$/) { next; } $mtime = (stat("wwwlng/$file"))[9]; if ($mtime >= $limittime) { push(@filelist2, "$mtime $file"); } } @filelist2 = sort { $b cmp $a; } @filelist2; foreach $g_filename (@filelist2) { $g_filename =~ s/^[0-9]+ //; &printFile($g_filename); } } sub printFile { $g_filename = $_[0]; $subject = ""; open(IN, "wwwlng/$g_filename") || print "Can't open $g_filename
    \n"; $subject = ; $subject =~ s/^Subject: //; $subject =~ s/[\r\n]$//; print "

    ■ $subject

    \n"; while () { chop($_); if ($_ =~ /^Reply-To: /) { next; } elsif ($head && $_ =~ /^From: /) { $_ =~ s/^From: //; print "$_\n"; } elsif ($head && $_ =~ /^E-Mail: /) { $_ =~ s/^E-Mail: //; print "Mail\n"; } elsif ($head && $_ =~ /^HomePage: /) { $_ =~ s/^HomePage: //; print "HomePage\n"; } elsif ($head && $_ =~ /^Date: /) { $_ =~ s/^Date: //; print "$_
    \n"; } elsif ($head && $_ =~ /^Solved: /) { print "[[解決]]
    \n"; } elsif ($_ eq "========================================") { print "
    \n"; $head = 1; } elsif ($_ eq "") { $head = 0; } else { $_ =~ s/(http:\/\/[a-zA-Z0-9\.\/\-+#_?~&%=^\@:]+)/$1<\/A>/g; print "$_
    "; } } print "
    \n"; print "
    [↑の記事に追加発言]\n"; print "
    \n"; close(IN); } # # サブジェクトを表示する # sub printSubject { # # 既存の発言を表示する # open(IN, "$g_dirname/$g_filename"); $_ = ; $_ =~ s/^Subject: //; $subject = 1; print "Content-type: text/html\n"; print "\n"; print "\n"; print "\n"; print "$_\n"; print "\n"; print "\n"; print "

    $_

    \n"; while () { chop($_); if ($subject && $_ =~ /^Reply-To: /) { next; } elsif ($head && $_ =~ /^From: /) { $_ =~ s/^From: //; print "$_\n"; } elsif ($head && $_ =~ /^E-Mail: /) { $_ =~ s/^E-Mail: //; print "Mail\n"; } elsif ($head && $_ =~ /^HomePage: /) { $_ =~ s/^HomePage: //; print "HomePage\n"; } elsif ($head && $_ =~ /^Date: /) { $_ =~ s/^Date: //; print "$_
    \n"; } elsif ($head && $_ =~ /^Solved: /) { print "[[解決]]
    \n"; } elsif ($_ eq "========================================") { print "
    \n"; $head = 1; $subject = 0; } elsif ($_ eq "") { $head = 0; } else { $_ =~ s/(http:\/\/[a-zA-Z0-9\.\/\-+#_?~&%=^\@:]+)/$1<\/A>/g; print "$_
    "; } } close(IN); # # 発言追加フォームを表示する # if ($g_filename =~ /^lng/) { return; } print "
    \n"; print "追加発言\n"; print "
    \n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; print "
    名 前:"; print "\n"; print "
    メール:"; print "\n"; print "
    URL:"; print "\n"; print "
    発 言:"; print " "; print "←解決(だろう)発言ならここをチェックしてください。
    "; print "
    \n"; print "\n"; print "\n"; print "\n"; print "
    \n"; print "\n"; print "
      \n"; print "
    • 誰でも自由に発言を追加してください。\n"; print "
    • 名前、メール、URLは省略してもかまいません。\n"; print "
    • HTMLのタグは使えません。そのまま表\示されます。\n"; print "
    • 再表\示を行うと発言が重複する事があります。\n"; print "
    \n"; print "
    \n"; } # # サブジェクトを管理者にメールで送信する # sub sendMail { open(OUT, "| $nkf -j | $sendmail $mailto"); print OUT "To: $mailto\n"; print OUT "Subject: $_[0]\n"; print OUT "\n"; open(IN, "$g_dirname/$_[0]"); while () { print OUT $_; } close(IN); close(OUT); print "メール送信を完了しました。"; } # # サブジェクトを削除する # sub deleteSubject { unlink("$g_dirname/$_[0]"); print "削除完了しました。"; } # # 自己テスト # sub localTest { print "
    OK. CGIは正常に動作しています。\n"; if (-d $lock_file) { print "
    NG. ロックファイル($lock_file)が残っています。\n"; print "rmdir で削除してください。\n"; } if ($manage ne "") { print "
    OK. 管理者モードが有効です。\n"; } if ($mailto ne "") { print "
    OK. メール送信機能が有効です。\n"; if (! -f $nkf) { print "
    NG. $nkfが存在しません。\n"; } if (! -f $sendmail) { print "
    NG. $sendmailが存在しません。\n"; } } } # # フォームに入力されたデータを読みこむ # sub readParam { read(STDIN, $query_string, $ENV{'CONTENT_LENGTH'}); @a = split(/&/, $query_string); foreach $x (@a) { ($name, $value) = split(/=/, $x); $value =~ tr/+/ /; $value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; $value =~ s/&/&/g; $value =~ s//>/g; $value =~ s/\n$//; &jcode'convert(*value, $kanji_code); $FORM{$name} = $value; } } # # 管理者用パスワードのチェック # sub passwdCheck { if ($_[0] ne $password) { print "管理者用パスワードが不正です。\n"; return(0); } else { return(1); } } # # ファイルロック開始 # sub fileLock { if ($file_lock_flag) { for ($i = 1; $i <= 6; $i++) { if (mkdir($lock_file, 755)) { last; } elsif ($i < 6) { sleep(2); } else { return(undef); } } } return(true); } # # ファイルロック解除 # sub fileUnlock { if ($file_lock_flag) { for ($i = 1; $i <= 4; $i++) { if (rmdir($lock_file) == 0) { last; } elsif ($i < 4) { sleep(1); } else { return(undef); } } } return(true); } # # プログラム中断時の処理 # sub sigexit { rmdir($lock_file); exit(0); } #======================================================================= # # 検索 # sub searchKeyword { # # 複数のワードを分割する # @words = split(/ +/, $FORM{'WORD'}); # # メタ文字を無効化する # for ($i = 0; $i <= $#words; $i++) { $words[$i] =~ s/([\+\*\.\?\^\$\[\-\]\|\(\)\\])/\\$1/g; } # # HTML文書を書き出す # print "

    Low Rider's ラウンジ(検索結果)

    \n"; print "
    \n"; if (defined($FORM{'WORD'})) { print "
    \n"; &search1($target_dir); print "
    \n"; print "
    \n"; print "検索が完了しました。\n"; } print "\n"; print "\n"; } # # すべてのファイルをなめ回す # sub search1 { local(@filelist, $dir); $dir = $_[0]; opendir(DIR, $dir); @filelist = readdir(DIR); closedir(DIR); foreach $file (@filelist) { if ($file eq ".") { next; } if ($file eq "..") { next; } $g_filename = "$dir/$file"; if (-d $g_filename) { &search1($g_filename); } else { &search2($g_filename); } } } # # ファイルの中身を検索する # sub search2 { local($target); $target = $_[0]; undef %wordflag; undef %wordtext; $title = ""; $match_count = 0; open(IN, $target); loop: while () { chop($_); if (($title eq "") && ($_ =~ /^Subject:/)) { $title = substr($_, 9); } foreach $word (@words) { if ($wordflag{$word} == 1) { next; } if (!($_ =~ /$word/i)) { next; } $_ =~ s/<[^>]*>//g; $wordtext{$word} = $_; if (($FORM{'ANDOR'} eq "or") || (++$match_count == $#words + 1)) { if ($title eq "") { $title = $target; } $target =~ s/wwwlng\///; print "

    \n"; print "

    "; print "$title\n"; print "
    "; foreach $wd (@words) { print "$wordtext{$wd} "; } print "\n"; last loop; } $wordflag{$word} = 1; } } undef @lines; close(IN); }