Script Note
VBやPerlなど、スクリプトに関する学習帖です。個人的に理解したことをまとめておく、倉庫のようなものです(^^)。
この文章は、Active Perlに同梱されているhtmlファイルの「Windows
Script Host」の中から、個人的に参考にしようと思った部分を抜き出して日本語でまとめ、さらに加筆修正を加えたものです。
すでにサーバーにあるファイルを添付してメールを送信するサンプルは、ちらほらと見つかるのですが、ローカルにあるファイルを添付して送信するコードが、あまり見つかりません。あっても、複数のスクリプトを合わせた巨大なもので、チョットした勉強には苦しいかなぁ(T_T)。まあそもそも、添付ファイルを送信しようとしていることが、チョットしたことじゃないよと言われれば、それまでのこと!
まとめたいことがいろいろありますが、今のところIEの起動、表示とリンクオブジェクトについてサラリと書いております。
タグのかっこにあたる文字「<」「>」をHTMLで表示する場合、「<」「>」という風に置き換えることになります。それを、Javascriptでやってみます。
WSHでの、フォルダ参照ダイアログの表示方法です。
添付ファイル付きメール送信
もともと、メールサイズが6KBに制限されているJフォンのロングメールに、より大きな添付ファイルを送りたいなと考えたのがきっかけです。ヘッダを含めて制限が6KBまでなので、普通のメーラーを使った場合、送りたいと思ったファイルが送信できない(添付ファイルは6KB以下だが、トータルでオーバーしている)ことがありました。
内容的には、まずPOSTメソッドでアドレスやメッセージなどと一緒にファイルを転送しています。ファイルはCGIと同じディレクトリに一旦書き込み、他の項目は配列$FORM{'xxxx'}に保存されます。それをsendmailに投げて送信した後、ファイルを削除するという流れです。サーバーへのデータ送信と、サーバーからのメール配信が切り分けて処理されているので、流れは把握しやすいと思います。
今後の取り組みとして、切り分けされている処理の融合(ファイルを書き込むことなく処理する)なんか、面白いかもしれないですね。
#!/usr/bin/perl
require './jcode.pl';
$mycgi = './filemail.cgi'; # このcgiファイル名
$myads = 'hoge@hoge.com'; # 送信者のアドレス
$sendmail = '/usr/sbin/sendmail'; # sendmailへのパス
if ($ENV{'REQUEST_METHOD'} eq "POST"){
&formget;
# 添付ファイルをフォームで増やしたら、$fname[x]の項目も増やせる
# 今の送信エンジンでは、できないんだけど(^^;
&sendMF($myads,$FORM{'EMAIL2'},$FORM{'SUBJECT'},$FORM{'MESSAGE'},$fname[0]);
unlink(@fname);
$message.= "To : $FORM{'EMAIL2'}<br>\n";
$message.= "Subject : $FORM{'SUBJECT'}<br>\n";
$message.= "Message :<br>$FORM{'MESSAGE'}<br>\n";
$message.= "AttachmentFile : @fname<br><br>\n";
$message.= "----- completed. -----<br>";
}
# HTMLの出力
print "Content-type: text/html\n\n";
print <<"_HTML_";
<html><head><title>メール送信フォーム</title></head>
<body>$message<br>
<form method="POST" enctype="multipart/form-data" action="$mycgi" >
<BR>送信先メールアドレス:
<BR><INPUT TYPE=text SIZE=50 NAME="EMAIL2">
<BR>
<BR>タイトル:
<BR><INPUT TYPE=text SIZE=50 NAME="SUBJECT">
<BR>
<BR>メッセージ:
<BR><TEXTAREA COLS=70 ROWS=6 NAME="MESSAGE"></TEXTAREA>
<BR>
<BR>添付ファイル:
<BR><input type=file size=100 name="filedat1">
<BR>
<BR><INPUT TYPE=submit VALUE=" 送信 "> <INPUT TYPE=reset VALUE=" クリア ">
</FORM>
_HTML_
exit;
# $FORM{'name'}に文字列を返し、また同一ディレクトリにファイルを取得する。
# 複数ファイルがあるときは、$fname[0]から順番にファイル名が保存される。
#
sub formget{
$filemode = 0; $flabel = 0; $name = '';
while(<STDIN>){
if($filemode == 1){
if(open( OUT, ">./$fname[$flabel]" )){
while(<STDIN>){
if(/^$bound/){ last; }
print OUT $_;
}
close( OUT );
}
$filemode = 0; $flabel++;
next;
}
if(/^--/ && $bound == ''){
$bound = $_;
$bound =~s/\r\n//;
}
if($_=~/^$bound/){
chomp($FORM{$name});
$name = '';
next;
}
if(/^Content-Disposition/){
$name = $_;
$name =~s/^Content-Disposition: form-data; name=\"(.*)\"(.*)$/$1/i;
$name =~s/^(.*)\";(.*)$/$1/i;
chomp($name);
if(/filename=/ && $_!~/filename=""/){
$fname[$flabel] = $_;
$fname[$flabel] =~s/^Content-Disposition: form-data; name=\"(.+)\"; filename=\"(.*)\"(.*)$/$2/i;
$fname[$flabel] =~s/ /_/g; #「 」を「_」に置換
$fname[$flabel] =~s/(.*)\\(.*)/$2/;
chomp($fname[$flabel]);
$filemode = 1;
}
$dummy = <STDIN>; #意味の無い行を読み飛ばす
next;
}
if ($name ne ''){
$FORM{$name}.= $_;
}
}
}
# 添付ファイル付きメール送信処理
#
sub sendMF{
# from to 件名 本文 添付file
my($from, $to, $subject, $comment, $file1) = @_;
my($baseType, $fdata1, $buf, $head, $msg0, $msg1, $part);
# 件名のエンコード
if($subject !~/^[\r\n]$/){
&jcode'convert(\$subject, 'jis');
$str = &encodeBase64($subject); # 自力でBase64Encode
$str =~ s/\n//g;
$subject = "=\?ISO-2022-JP\?B\?$str\?=";
}
if($comment !~/^[\r\n]$/){
&jcode'convert(\$comment, 'jis'); # 本文をJISコードに変換
# my 宣言された変数の型グロブはないので、*abcのような記載ができない
# \$abcのように書くと、間違いが起きにくいらしい
$part++;
}
if($file1 ne ''){ # 添付ファイル処理
# ファイル情報取得
my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
$atime, $mtime, $ctime, $blksize, $blocks) = stat($file1);
# 作業バッファに読込
open(IN, $file1);
binmode(IN);
read(IN, $buf, $size);
close(IN);
$fdata1 = &encodeBase64($buf); # 自力でBase64Encode
$part++;
$part++;
}
# 状態に応じてバウンダリ設定
if($part <= 1){ # 添付ファイルが無い場合
$baseType = &planeHeader;
$msg0 = $comment;
}
$buf = &attachHeader($file1);
if($part == 2){ # 添付ファイルのみの場合
$baseType = $buf;
$msg1 = $fdata1;
}
if($part == 3){ # マルチパートの場合
$boundary = &makeBound($comment);
$baseType = &multiHeader($boundary);
$dummy = &planeHeader;
$msg0 =<<"__EOF__";
--$boundary
$dummy
$comment
__EOF__
$msg1 =<<"__EOF__";
--$boundary
$buf
$fdata1
--$boundary--
__EOF__
}
# ヘッダ
my($head) = <<"__EOF__";
To: $to
From: $from
Subject: $subject
MIME-Version: 1.0
$baseType
__EOF__
# メール送信
open(MAIL, "| $sendmail -t -i -f $from");
print MAIL "$head$msg0$msg1";
close(MAIL);
}
# base64エンコード
sub encodeBase64{
# Base64 エンコード/デコード用キャラクタセット
$base64Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
my($len, $idx, $num, $ch, $ord, $bit24, @xx, $result, $rest, $cnt, $str4);
my($temp) = "$_[0]\0\0";
$result ="";
if($_[0] eq "") {return $result;}
$len = length($temp);
$idx = 0;
$cnt = 0;
while($idx < $len){
$ord = ord(substr($temp, $idx, 1));
$idx++;
$num = $idx % 3;
$xx[$num] = $ord;
if($num == 0){
$rest = $len - $idx;
$bit24 = ($xx[1] << 16) + ($xx[2] << 8) + $xx[0];
if ($rest < 2){ $ch = "="; }
else { $ch = substr($base64Char, ($bit24 & 63), 1); }
$str4 = $ch;
$bit24 = ($bit24 >> 6);
if ($rest < 1){ $ch = "="; }
else { $ch = substr($base64Char, ($bit24 & 63), 1); }
$str4 = $ch . $str4;
$bit24 = ($bit24 >> 6);
$ch = substr($base64Char, ($bit24 & 63), 1);
$str4 = $ch . $str4;
$bit24 = ($bit24 >> 6);
$ch = substr($base64Char, ($bit24 & 63), 1);
$str4 = $ch . $str4;
$result .= $str4;
if($cnt == 15){
$result .= "\n";
$cnt = 0;
}else{
$cnt++;
}
if($rest < 3){ last; }
}
}
return "$result\n";
}
# バウンダリの作成
sub makeBound{
my($i, $a, $b, $c);
srand;
$a = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
while(1){
$b = '';
for($i=0; $i<32; $i++){
$c = substr($a,int(rand(length($a))),1);
$b.= $c;
}
# バウンダリがデータの中にないか
if($_[0] !~/$b/){ last; }
}
return "$b";
}
sub attachHeader{
my($file) = $_[0];
my($result);
$result =<<"__EOF__";
Content-Type: application/octet-stream; name="$file"
Content-Disposition: attachment; filename="$file"
Content-Transfer-Encoding: base64
__EOF__
return "$result";
}
sub planeHeader{
my($result);
$result =<<"__EOF__";
Content-Type: text/plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit
__EOF__
return "$result";
}
sub multiHeader{
my($head) = $_[0];
my($result);
$result =<<"__EOF__";
Content-Type: multipart/mixed; boundary=$head
Content-Transfer-Encoding: 7bit
__EOF__
return "$result";
}
IEをWSHで操作する
まずは、基本的なことから。
' 最初に、IEのオートメーションオブジェクトを取得。IEを操作するための構えですね。
Set objIE = CreateObject("InternetExplorer.Application")
' IEを起動し、URLの内容を読み込む。
objIE.navigate2("http://www.yahoo.co.jp")
' IEを表示する。
objIE.visible = true
表示させるだけなら、結構簡単にできます。特に難しいこともないですね。
続けて、以下のコードをつなげてみましょう。
' 指定したURLが読み込み中かを判定。完了するまで待機する。
Do While objIE.busy
Wscript.sleep 100
Loop
' 表示された内容から、<a href= 〜 ></a> でいくつリンクが張られているかを取得、表示する。
a = objIE.document.links.length
msgbox("Yahoo! からリンクされている、URL" & a & "個を表示します。")
str = "<html><head><title>Yahoo! からリンクされている URL</title></head><body>" & vbCrLf
for i = 0 to a - 1
str = str & objIE.document.links(i).href & "<br>" & vbCrLf
next
str = str & "</body></html>"
objIE.document.write(str)
Yahoo! からリンクされているURLの数と内容が表示されたかと思います。読み込み完了を待って、リンクオブジェクトを用いて実現しています。リンクオブジェクトには、以下のようなものがあります。
・objIE.document.links.length
リンクオブジェクト(<a href= 〜 ></a>)の個数を示します。
・objIE.document.links(n)
それぞれのオブジェクトは、links(n) で参照します。
・objIE.document.links(n).href
<a href= "★">●</a> の ★ の部分を示します。
・objIE.document.links(n).innerText
<a href= "★">●</a> の ● の部分を示します。
HTMLタグを置換する
通常であれば、Perlの強力な正規表現を用いるところですが、ローカルの環境で手軽に行いたい場合は、VBscriptやJavascriptなどで正規表現オブジェクトを使うことになると思います。この正規表現オブジェクト、Perlに慣れているとかなり回りくどい記述をしなければならず、ちょっとメンドウです。
しかし、Javascriptであれば、オブジェクトでなくとも正規表現が利用できます。今回は、その方法を使ってコードを書いてみます。
<html>
<head><title>HTMLタグを置換する</title>
<script language="javascript">
<!--
function repTag(str){
str = str.replace(/&/g , "&");
str = str.replace(/</g , "<" );
str = str.replace(/>/g , ">" );
str = str.replace(/\t/g, " " );
rep.code.value = str;
}
// -->
</script>
</head>
<body>
<form name="rep">
<textarea cols=70 rows=10 name="code"></textarea>
<br><br>
<input type="button" value=" Replace " onClick=repTag(rep.code.value)>
</form>
</body>
</html>
stringオブジェクトのreplaceメソッドで、第一引数に検索に使用する正規表現のパターンを設定し、第二引数に置換する文字列を設定します。置換された文字列は、返り値として戻ります。一致するパターンが無い場合、元の文字列がそのまま返ります。
以下は、サンプルです。上のコードをペーストして、試してみてください。
なお、stringオブジェクトにはmatchメソッドがあり、正規表現を使った普通の検索も当然に行うことができます。利用方法は、stringオブジェクトのmatchメソッドの引数に、検索に使用する正規表現のパターンを設定します。検索の結果、該当するものがあればその文字列を、なければ「null」を返します。WSHでのサンプルです。
str = new Array("Javascript", "21th");
for(i = 0; i < str.length; i++){
if(str[i].match(/^[0-9]/)){
WScript.Echo(str[i] + "は、数字から始まっています!");
}else{
WScript.Echo(str[i] + "は、数字以外の文字からです。");
}
}
フォルダ参照ダイアログを表示する
VisualBasicなんかだと、あれこれ記述してやっと使えるフォルダ参照ダイアログ。VBscriptでは、かなり簡単に使うことができます。まさに、Tipsですね。
Set Shell = CreateObject("Shell.Application")
Set objFolder = Shell.BrowseForFolder(0, "フォルダを選択してね!", 1, "c:\\")
if objFolder is Nothing then
Msgbox("フォルダを選択してください")
else
Msgbox(objFolder.Items.Item.Path)
end if