#!/usr/bin/perl # # 採用希望者資料請求受け付け CGI # # 井汲 景太, 2000, 2/11 # revised 2001,7/24 年齢を入力項目に追加 # 2002,4/17 cross site scripting 対策 # 2002,6/18 チューター・講師応募の際、 # 学部・学科名も入力してもらう。 # 2003,6/10 jt mail to upd jinji->kikakujinji # # $Id: apply,v 1.3 2001/07/24 15:07:52 keita Exp $ # # 年 $YEAR=2002; # メールの送り先。 $REPORT_TO = 'kikakujinji@seg.co.jp'; # form 変数のうち、必須であるものの一覧。 @mandatory = ("name1", "name2", "ruby", "age", "zip", "address", "apply_type"); # 以下の変数は、form 変数名=perl 変数名。 @scalars = ("name1", "name2", "ruby", "age", "zip", "address", "tel1", "tel2", "tel3", "tel4", "tel5", "tel6", "email", "url", "comment", "apply_type", "gakubu", "gakka", "introducer1", "introducer2", "others"); # 用途 specific なサブルーチンは、 # form2perl (form 変数を perl 変数に代入) # check_cond (送られたデータのエラーチェック) # sort_data (送られたデータのソート) # write_csv (CSV ファイルに書き出し) # send_mail (送られたデータをメールで送信) # show_preview(プレビュー画面の出力) # thanks (「ありがとう」メッセージの表示) # error (エラーメッセージの表示) # である。本スクリプトを他の目的に転用するときには、これらの # サブルーチンを書き換える必要がある。 # # file lock は、プレビュー時には行わない。本送信時にカウンタファイルを # 読み出すとともに、これによりロックを行う。ロック用の file handle は # SEQ であり、これはグローバル変数。それ以外のファイルに対するロックは # 行わない。 # 設定ファイルやデータファイルの置場。 $BASE_DIR = "/var/spool/www/formdata/apply" . $YEAR; # CSV ファイル名。 $CSV_FILE = $BASE_DIR . "/" . "data.csv"; # sendmail binary $SENDMAIL="/usr/sbin/sendmail"; $SENDMAIL_FLAG="-t -oi -om"; # 管理者の名前とアドレス。 $ADMIN_NAME = 'SEG WWW 管理者(お問い合わせはこちらへ)'; $ADMIN_ADDR = 'webmaster@seg.co.jp'; # 初期化 # o form data のバラし # o HTTP header の出力 # o perl パッケージの読み込み # など。 &init(); # 前処理 # o form data を perl の変数に代入。 # o 日本語コード変換。 # o 送られてきたデータのエラーチェック(必須入力項目が欠けていないか、など) # o 本投稿かどうかの判定。 # o 本投稿だったなら登録番号の読み込みとファイルのロック。 &pre(); # 本処理 # o 本投稿なら CSV へ書き出し、メール送信。 # o プレビューならプレビュー画面の出力。 &body(); # 後処理 # o 本投稿だったなら「ありがとう」画面の出力。 # o HTML footer の出力。 # o 本投稿だったならロックの解除。 &finish(); exit 0; sub pre { # form 変数を全部収める連想配列を %forms とする。 # 各要素は list になっていて、そのまた要素が form 変数の値。 # form data のコード変換。 %forms = &code_convert_form(); # form 変数の perl 変数への代入。 &form2perl(); # 送信されたデータに記入漏れなどのエラーはないか。 &check_cond(); # 送信データのソート。 &sort_data(); # 本投稿かどうか。 $direct = param('direct'); # 日付情報の取得。 ($year, $month, $day)=&get_date(); } sub body { # 本投稿なら CSV へ書き出して、メール送信。 if ($direct) { # 何番目かのデータかを読み込み、ファイルをロックする。 $num = &read_lock() if ($direct); &write_csv(); &send_mail(); } else { # プレビューなら、プレビュー画面を出力。 &show_preview(); } } sub finish { # 本投稿なら「ありがとう」メッセージを表示。 if ($direct) { &thanks(); } # HTML footer の出力。 &html_footer(); # 本投稿ならロックを解除。 if ($direct) { &release_lock(); } } sub code_convert_form { # form 変数を全部収める連想配列。各要素は list になっていて、 # そのまた要素が form 変数の値。 my %forms; # 作業用変数 my $p; # form 変数のすべてを EUC コードにして、半角カナは全角に。 # 全角数字は半角に。 foreach $p (param()) { foreach (param("$p")) { jcode::convert(\$_, "euc"); jcode::h2z_euc(\$_); jcode::tr(\$_, "0-9", "0-9"); push @{$forms{"$p"}}, $_; } } return %forms; } sub form2perl { # form 変数を perl の変数に代入。 # loop 変数。 my $i=0; # 以下の変数は、form 変数名=perl 変数名。 foreach (@scalars) { $$_ = ${$forms{"$_"}}[0]; } # 以下は、perl 変数を list にするもの。 # 応募科目・講師のみ @kamoku = @{$forms{"kamoku"}}; %shokushu_label = ("staff" => "事務職員", "teacher" => "講師", "tutor" => "チューター"); # %kamoku_label = ( "mathh" => "高校数学", "mathj" => "中学数学", # "eng" => "英語", # "phys" => "物理", "chem" => "化学" ); } %clue_label = ("newspaper" => "新聞広告", "web" => "web", "seg_concerned" => "SEG関係者の紹介", "friends" => "知人の紹介", "others" => "その他"); sub check_cond { # loop 変数。 my $i=0; # 必須になっている form 変数が空でないかチェック。 foreach (@mandatory) { if (! param("$_")) { &error("hissu"); } } # 構造チェック。 # 講師・チューター希望の場合、希望科目が空でないか? if ( $apply_type eq "teacher" || $apply_type eq "tutor") { &error("empty_kamoku") if (&empty(@kamoku)); } # 講師・チューター志望の場合、学歴は空でないか? if ( $apply_type eq "teacher" || $apply_type eq "tutor") { &error("empty_gakureki") if ($gakubu eq "" || $gakka eq ""); } # 電子メールアドレスとして不正な形ではないか? # if (&is_email($email) == 0) { # &error("invalid_email"); # } } sub sort_data { } sub get_date { my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime; return ($year+1900, $mon+1, $mday); } sub read_lock { my $seq; open(SEQ, "+<$sequence") or &error("busy"); flock(SEQ, 2); # 2=排他的ロック。 if ($seq = ) { $seq++; } else { $seq = 1; } return $seq; } sub write_csv { # 改行を LF にすべきか? my $newline = "\r\n"; # 日本語コードの変換はそれを使う側でやってもらおう。 # ループ変数。 my $i=0; open(CSV, ">>$CSV_FILE") or die "Cannot open csv file for appending: $!"; local($,) = ","; # 出力項目の区切り文字。 print CSV $num, $year, $month, $day, $name1, $name2, $ruby, $age, $zip, $address, $tel1, $tel2, $tel3, $email, $tel1, $tel2, $tel3, $tel4, $tel5, $tel6, $clue, $introducer1, $introducer2, $others, (&url_nonempty($url)) ? $url : "", $shokushu_label{"$apply_type"}, join('+', @kamoku), $gakubu . $gakka, ""; # 最後にコメント。 $_ = $comment; s/\n//g; # 改行コードを除去。 print CSV $_; print CSV $newline; close(CSV); } sub send_mail { # 報告内容をメールで送る。 # メールの内容。あとで MIME encode と日本語コード変換をしたいから、 # 直接パイプには送らずに変数に蓄える。 my $header = &make_mail_header(); my $body =&make_mail_body(); # 日本語コード変換。 jcode::euc2jis(\$header); jcode::euc2jis(\$body); # MIME encode。 $header = &mimeencode($header); open(MAIL, "|$SENDMAIL $SENDMAIL_FLAG") or die "Cannot execute sendmail: $!"; print MAIL $header; print MAIL "\n"; print MAIL $body; close(MAIL); } sub make_mail_header { # メール送信者のアドレスは、もし未記入なら管理者アドレスにする。 my $fromaddr = ($email || $ADMIN_ADDR); my $tmp = < Subject: 【人事資料請求: No. $num】 EOD return $tmp; } sub make_mail_body { # ループ変数。 my $i; # 日付文字列 my $nowstring = sprintf("%4d/%02d/%02d", $year, $month, $day); my $tmp = sprintf("No.[%05d]\n [%s] [%s歳] 自宅tel.[%s-%s-%s] 携帯.[%s-%s-%s]\n", $num, $ruby, $age, $tel1, $tel2, $tel3, $tel4, $tel5, $tel6); $tmp .= sprintf(" [%s %s] [〒%s %s]\n", $name1, $name2, $zip, $address); $tmp .= "\n"; $tmp .= sprintf(" 請求日 [%s] 電子メールアドレス [%s]\n", $nowstring, ($email) ? $email : "記入なし"); $tmp .= sprintf(" URL [%s]\n", (&url_nonempty($url)) ? $url : "記入なし"); $tmp .= "\n"; $tmp .= sprintf("希望職種 [%s]\n", $shokushu_label{"$apply_type"}); my $label; my @list; if ($apply_type eq "teacher" || $apply_type eq "tutor") { $tmp .= sprintf(" 科目: "); $tmp .= join("/", @kamoku); $tmp .= sprintf(" 最終学歴: %s%s", $gakubu, $gakka); } $tmp .= "\n"; $tmp .= sprintf("募集を知ったきっかけ [%s]\n", $clue_label{"$clue"}); $tmp .= sprintf("[%s %s %s]\n", $introducer1, $introducer2, $others); $tmp .= "\nコメント\n$comment" if ($comment); return $tmp; } sub show_preview { # プレビュー画面の出力。 # ループ変数。 my $i=0; print <

SEG 応募資料請求・確認

以下のデータでよろしいですか?

  • 修正したい場合はブラウザのバック機能で一つ前のページに戻り、 再度送信してください。
  • これでよろしければ、本ページ下部の「データを SEG に登録する」を 選択してください。

HTML_PREVIEW printf("
●氏名
%s %s%s)\n", &html_escape_list($name1, $name2, $ruby)); printf("
●年齢
%s\n", &html_escape($age)); printf("
●住所
〒%s %s\n", &html_escape_list($zip, $address)); printf("
●自宅電話番号
%s-%s-%s\n", &html_escape_list($tel1,$tel2,$tel3)); printf("
●携帯電話番号
%s-%s-%s\n", &html_escape_list($tel4,$tel5,$tel6)); printf("
●募集を知ったきっかけ
%s\n", $clue_label{"$clue"}); printf("
●E-Mailアドレス
%s\n", &html_escape(($email) ? $email : "(記入なし)")); printf("
●個人ウェブページ URL
%s\n", &html_escape((&url_nonempty($url)) ? $url : "(記入なし)")); printf("
●応募職種
%s\n", $shokushu_label{"$apply_type"}); if ($apply_type eq "teacher" || $apply_type eq "tutor") { print "
●希望科目\n"; foreach (@kamoku) { printf("
%s\n", &html_escape($_)) if ($_); } print "
●最終学歴\n"; printf("
%s%s\n", &html_escape_list($gakubu, $gakka)); } print "
\n"; if ($comment) { print <●SEG へのご質問・自己アピールなど
HTML_PREVIEW

    print &html_escape($comment);
    print "
\n\n"; } print < HTML_PREVIEW my $var=""; foreach $var (@scalars) { printf(' %s', $var, &html_escape(${$var}), "\n"); } print "\n"; foreach (@kamoku) { printf(' %s', &html_escape($_), "\n") if ($_); } print <
一度だけ押して、しばらくお待ちください。二度押すと、 同じデータが二度送られてしまいます。
HTML_PREVIEW } sub empty { foreach (@_) { return 0 if ($_); } return 1; } sub url_nonempty { $_=shift; return ($_ && $_ ne "http://"); } sub html_escape { $_ = shift; s/&/&/g; s//>/g; s/"/"/g; s/\r//g; return $_; } sub html_escape_list { my @tmp=(); my $tmp=""; foreach $tmp (@_) { push @tmp, &html_escape($tmp); } return @tmp; } sub thanks { # 「ありがとう」メッセージ。 print <

ありがとうございました。

資料を送付いたしますので、今しばらくお待ちください。

SEG のトップページへ

THANKS } sub http_begin { # HTTP header の出力。 print "Content-Type: text/html; charset=EUC-JP\n\n"; } sub html_footer { # HTML footer の出力。 print <
$ADMIN_NAME <$ADMIN_ADDR>
HTML_FOOTER } sub release_lock { # アクセス番号の更新。 seek(SEQ, 0, 0) or die "Cannot rewind sequence number file: $!"; truncate(SEQ, 0) or die "Cannot truncate sequence number file: $!"; print SEQ $num or die "Cannot write sequence number file: $!"; close(SEQ); # close したら FILE handle にはアクセスできぬはず…。 # close すると自動的に unlock されるのか? #flock(SEQ, 8); # 8=アンロック } sub error { my $which=shift; print <

エラー

ERROR_HEADER if ($which eq "hissu") { print <必須項目欠如

必須の入力項目に入力が欠けています。ブラウザのバック機能で前のページに 戻って、必須項目をすべて埋めてから再度送信してください。

ERROR } elsif ($which eq "empty_kamoku") { print <科目名欠如

講師・チューター希望の方は、希望科目を必ず選択してください。 ブラウザのバック機能で前のページに戻り、選択してから再度送信 してください。

ERROR } elsif ($which eq "empty_gakureki") { print <学部・学科名欠如

講師・チューター志望の方は、学部・学科を必ず入力してください。 ブラウザのバック機能で前のページに戻り、入力してから再度送信してください。

ERROR } elsif ($which eq "busy") { print <混雑中

ただいま、システムが混み合っております。しばらく時間を おいてから、再度送信してくださいますようお願いいたします。

なお、しばらくしてからも症状が回復しない場合、大変お手数ですが、 下記の管理者までご連絡ください。

ERROR } else { die "Unknown error type [$which] has occurred. Please report this error to the administrator written below.\n"; } &html_footer(); &release_lock() if ($direct); exit 1; } sub init { # CGI handling routines の利用を宣言。 # これだけで、form 変数の取り込みなどを全部やってくれる。 use CGI ":cgi"; # エラーがブラウザに見えるように。 use CGI::Carp 'fatalsToBrowser'; # Denial of service attack の防御。 $CGI::POST_MAX=1024 * 100; # max 100K posts $CGI::DISABLE_UPLOADS = 1; # no uploads # 日本語コード変換ルーチンの利用を宣言。 require "jcode.pl"; # MIME encoding 用変換ルーチンの利用を宣言(メール送信用)。 require "mimew.pl"; # ディレクトリ移動 chdir($BASE_DIR) or die("Unable to change to base directory $BASE_DIR: $!"); # HTTP header や、HTML header の出力。 &http_begin(); # データ番号ファイル名 $sequence="number"; }