人気ブログランキングへ

2007年05月25日

メールの冒頭のみを携帯電話に転送する

以前、Procmail を使って PC で受信したメールを携帯電話に転送する方法について書いたが (「PC←→携帯間メール転送 (fetchmail & procmail)」)、パケット定額にしていないので通信費がバカにならないことがわかった。
せめて、メールの冒頭のみ転送すればよいことに気づき、そのためのスクリプトを書いてみた。
#!/usr/bin/perl
#==============================================================================
# メールの冒頭部分テキストを出力する
#------------------------------------------------------------------------------
# 使い方:
# cat MAIL_FILE | mail_firstlines 10
# メールファイル MAIL_FILE の本文冒頭 10 行分を標準出力
#==============================================================================
my $header = '';
#------------------------------------------------------------------------------
# 表示行数の決定
#------------------------------------------------------------------------------
my $lines = 1;
if ($#ARGV >= 0) {
$lines = $ARGV[0];
$lines =~ /^\d+$/ or $lines = 1;
}
#------------------------------------------------------------------------------
# メールヘッダをスキャンし、ヘッダ全体を $header に格納する
# ※Content-Type: ヘッダはあとで変換する都合があり、1行にまとめている
#------------------------------------------------------------------------------
my $content_type = '';
my $content_type_stat = 0;
my $to_stat = 0;
while (<STDIN>) {
s/(?:\r\n?|\n)$//;
last if $_ eq '';
if (/^content-type:\s*(.+)$/i) {
# Content-Type: ヘッダ
$content_type_stat = 1;
$content_type = $1;
} elsif (/^\s/ and $content_type_stat) {
# Content-Type: の続き
s/^\s+//;
$content_type .= $_;
} else {
if ($content_type_stat) {
$header .= "Content-Type: $content_type\n";
}
$header .= "$_\n";
$content_type_stat = 0;
}
}
if ($content_type_stat) {
$header .= "Content-Type: $content_type\n";
}
#------------------------------------------------------------------------------
# Content-Type が text/* のメールは、指定された行数出力
#------------------------------------------------------------------------------
if ($content_type =~ /^text\//i) {
print $header;
print "\n";
# print first lines and exit.
for (my $i = 0; !eof(STDIN) and $i < $lines; $i++) {
$_ = <STDIN>;
s/(?:\r\n?|\n)$//;
# 空行または>で始まる行、最初の挨拶文はとばす
if ($_ eq '' or /^>/ or ($i == 0 and aisatu($_))) {
--$i;
} else {
print "$_\n";
}
}
exit;
}
#------------------------------------------------------------------------------
# Content-Type が multipart/* のメールは、各パートから text/* のものを探す
#------------------------------------------------------------------------------
elsif ($content_type =~ /^multipart\//i) {
# boundary を調べる
$content_type =~ /boundary\s*=\s*([^;]+)/i;
my $boundary = $1;
$boundary =~ s/^\"(.+)\"$/$1/;
# パターンマッチに使うので、エスケープ
$boundary =~ s/([\(\)\[\]\+\*\.])/\\$1/g;
# 各パートを調べる
LOOP1:
while (!eof(STDIN)) {
while (<STDIN>) {
# パート先頭を検知
last if /^\-\-$boundary/;
}
# パートの Content-Type を調べる
$content_type = '';
$content_type_stat = 0;
while (<STDIN>) {
s/(?:\r\n?|\n)$//;
last if $_ eq '';
if (/^content-type:\s*(.+)$/i) {
# Content-Type: ヘッダ
$content_type_stat = 1;
$content_type = $1;
} elsif (/^\s/ and $content_type_stat) {
# Content-Type: の続き
s/^\s+//;
$content_type .= $_;
} else {
$content_type_stat = 0;
}
last LOOP1 if eof(STDIN);
}
#----------------------------------------------------------------------
# text/* のパートの場合
#----------------------------------------------------------------------
if ($content_type =~ /^text\//i) {
# メールヘッダを、このパートのヘッダに変更
$header =~ s/^Content-Type: .+$/Content-Type: $content_type/m;
# ヘッダを出力
print $header;
print "\n";
# パートの冒頭から指定された行数出力
for (my $i = 0; !eof(STDIN) and $i < $lines; $i++) {
$_ = <STDIN>;
s/(?:\r\n?|\n)$//;
# 空行または>で始まる行、最初の挨拶文はとばす
if ($_ eq '' or /^>/ or ($i == 0 and aisatu($_))) {
--$i;
} else {
print "$_\n";
}
}
exit;
}
}
#--------------------------------------------------------------------------
# text/* のパートが見つからない場合は、空メール
#--------------------------------------------------------------------------
$header =~ s/^Content-Type: .+$/Content-Type: text\/plain/m;
print $header;
print "\n";
}
#------------------------------------------------------------------------------
# それ以外は空メール
#------------------------------------------------------------------------------
else {
if ($content_type eq '') {
$header .= "Content-Type: text/plain\n";
} else {
$header =~ s/^Content-Type: .+$/Content-Type: text\/plain/m;
}
print $header;
print "\n";
}
# 挨拶文かどうか判定
sub aisatu
{
my $str = $_[0];
return
(
$str =~ /\x24\x35\x24\x5e/ # さま
or $str =~ /\x24\x35\x24\x73/ # さん
or $str =~ /\x4d\x4d/ # 様
or $str =~ /\x24\x2a\x40\x24\x4f\x43/ # お世話
);
}
exit;
※空行または>ではじまる行をとばす処理を redo if ($_ eq '' or /^>/) としていたが、そこで処理が停止してしまう症状が起きるので、上記のように改めた (2007-06-06)。原因は不明。

使い方は、これをたとえば mail_firstlinesというファイル名で保存してパーミションを正しく指定し、
$ cat mail_file | mail_firstlines 10 | /usr/sbin/sendmail 携帯メールアドレス
のように実行する。
Procmail で実行させるのであれば、.procmail に、
:0c | mail_firstlines 10 | /usr/sbin/sendmail 携帯メールアドレス
などと記述すればよい。
mail_firstlines は、パラメータに転送する本文の行数をとる。省略すると、1行だけ送る。
コンテンツがテキスト以外の場合は、本文が空のメールを送る。 添付ファイルがある場合 (Multipart の場合) は、テキストの添付ファイルを探して送る (ただし、1階層のみに対応)。
mail_firstlines は、Content-Type 以外はヘッダに変更を加えていない。

【変更履歴】
2007-06-07
コードに aisatu 関数を追加し、最初の挨拶文を含む行を除去するようにした
posted by K/I at 14:37 | 東京 ☔ | Comment(0) | TrackBack(0) | 技術メモ | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:


この記事へのトラックバック