[Edit][Create New]
[ IndexPage / ネットとプログラミング / CGI / 便利なスクリプト片(未整理) / ParseMIME.pm ]

ParseMIME.pm

#!/usr/local/bin/perl

package ParseMIME;

use Tool;
use strict;

my $CRLF = '\x0d\x0a';

# 1で非RFC準拠のドキュメントを許す
# 主にIEがヘッダに8ビット文字を含める問題に対応
sub IE_COMPAT{1};

# MIMEメッセージへの参照を受け取って、構文解釈を行って返す
# $return{''}は、メッセージボディ。$return{'要素名'}{''}はその要素の値。
# $return{'項目名'}{'属性名'}その要素の属性'属性名'の値

sub parse_mime{
	my($part) = @_;
	my %return;

	# RFC的定義の正規表現
	my $reg = rfc_regex();

	# pertを行ごとに分割したもの
	my @part = split(/$CRLF/,$$part);
	my %header;

	# whileループで使う
	my($line, $key);

	# ヘッダごとに連想配列に格納する。
	while($_ = shift(@part)){
		last if /^$/; # ヘッダの終わり
		chomp;

		# X-要素はX-の後にtokenを続ける事が出来るので、token扱いしておく
		if (/^($reg->{token}): (.+)/i){
			$key = "\L$1";
			$header{$key} = $2;
		}elsif(s/^\s+/ /){
			$key or Tool::error("malformed header line: $_<br>continuous line have no parent.");
			# 継続行の場合、追加する
			$header{$key} .= $_;
		}else{
			Tool::error("malformed header line: $_");
		}
	}

	# メッセージ・ボディ
	$return{''} = join('', @part);
	undef @part;

	foreach my $key (keys %header){
		$return{$key} = parse_param($header{$key});
	}

	return(\%return);
}

# ヘッダラインの「: 」以降を解釈。値をバラして戻す
sub parse_param{
	my($header_line)= @_;

	my %return;
	my $reg = rfc_regex();

	my @param = split(/; /,$header_line);
	# まずヘッダラインの値を格納
	$return{''} = shift(@param);

	# パラメタの処理
	foreach(@param){
		if(/^($reg->{token})=($reg->{token})/){
			# 値はトークン
			$return{"\L$1"} = $2;
		}elsif(/^($reg->{token})=($reg->{quoted_str})/){
			# 値はquoted-string
			my ($key, $value) = ("\L$1", $2);
			# 値を元に戻す(クオートを外す)
			$value =~ s/^"(.*)"$/$1/;
			$value =~ s/\\(.)/$1/g;
			$return{$key} = $value;
		}else{
			Tool::error("malformed param: '$_' on '$header_line'");
		}
	}
	\%return;
}

# RFC的定義の正規表現
# ここで実際に使うのはtokenとquoted_str
sub rfc_regex{
	my %rfc_reg; # 戻り値

	# 便宜上。
	my $bksl = "\\\\"; #シングルクオートでも同じ。'\\'が入る。

	# RFC1521 http://www.ietf.org/rfc/rfc1521.txt

	# tspecials :=  "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / "\" / <"> / "/" / "[" / "]" / "?" / "="
	$rfc_reg{tspecials} = qq{\\(\\)\\<\\>\\@\\,\\;\\:\\"\\/\\[\\'\\?\\=$bksl};
	# token  :=  1*<any (ASCII) CHAR except SPACE, CTLs, or tspecials>
	$rfc_reg{token} = "(?:[^\x00-\x20\x7f-\xff$rfc_reg{tspecials}]+)"; # 

	# RFC822 http://www.ietf.org/rfc/rfc822.txt
	
	# qtext = <any CHAR excepting <">, "\" & CR, and including linear-white-space> ; may be folded
	$rfc_reg{qtext} = qq{(?:[^$bksl\\"\x0d\x80-\xff])}; 
	# quoted-pair = "\" CHAR ; may quote any char
	$rfc_reg{quoted_pair} = qq{(?:${bksl}} . qq{[\x00-\x7f])}; # "\" CHAR
	# quoted-string = <"> *(qtext/quoted-pair) <">; 
	$rfc_reg{quoted_str} = qq{(?:\"$rfc_reg{qtext}*(?:$rfc_reg{quoted_pair}$rfc_reg{qtext}*)*\")}; 

	# ゆるいquoted_srtを作る。IE対応用
	if(IE_COMPAT){
		$rfc_reg{qtext} = qq{(?:[^\\"\x0d])}; 
		$rfc_reg{quoted_str} = qq{(?:\"$rfc_reg{qtext}*(?:$rfc_reg{quoted_pair}$rfc_reg{qtext}*)*\")}; 
	}
	
	\%rfc_reg;
}

1;