目次
背景
Mac OS XにバンドルされているメーラのMailは10.3版と10.4版では
メールを格納するファイルの形式が異なっている。10.3はmboxだったが、10.4ではemlxという
拡張子になってバラバラでディレクトリに格納されている。
googleで探ってみると情報がたくさんあり、既にtoolが存在している。
単なる興味本位と暇つぶしにperlでemlx 2 mboxを書いてみた。
解説
TBD
- 解説が欲しい、ソースが欲しい、直して欲しい等あればコメントしてください。 --
src
#!/usr/bin/perl
use Carp;
use strict;
# grobal varliables
my $targetDir;
my $mboxFilename;
# grobal constant valiables
my $MBOX_DEFAULT_NAME = "mbox";
if ($#ARGV < 0) {
&usage();
} elsif ( !(-e $ARGV[0]) ) {
&usage("$ARGV[0] is not exist\n");
} elsif ( $ARGV[1] ) {
$mboxFilename = $ARGV[1];
}
$targetDir = $ARGV[0];
&mainloop($targetDir, $mboxFilename);
exit(0);
########################################
### main routines ###
sub mainloop {
my ($i);
my $tdir = $_[0];
my $tname = $_[1];
if ( $tname =~ /^\s{0,}$/ || length($tname) < 1 ) {
$tname = $MBOX_DEFAULT_NAME;
}
# ファイル一覧の取得
opendir(DIR, "$targetDir") or die "$!\n";
my $file;
my @files;
while ( ($file = readdir(DIR)) ) {
push(@files, $file) unless ( -d $file );
}
closedir(DIR);
# mboxのオープン。既存があればとりあえずここで終了する。
my $wr = \*WR;
if ( -e $tname ) {
printf("%s is exist, so aborting.\n", $tname);
exit(1);
} else {
open($wr, ">$tname") or die "$!\n";
}
# mboxへのコピー
my $flen;
my $fpath;
my $mboxlen = 0;
for ( $i = 0; $i < $#files +1; $i++ ) {
$fpath = "$tdir/$files[$i]";
$flen = -s $fpath;
if ( $flen > 0 ) {
$mboxlen += &writeMbox($wr, $fpath, $mboxlen);
printf("do %s(%d) | %s(%d)\n",
$fpath, $flen, $tname, $mboxlen);
} else {
printf("%s filesize is %d, so ignore\n",
$fpath, $flen);
}
&reportProgress($#files+1, $i+1);
}
}
### sub routines ###
sub writeMbox {
my $wr = $_[0];
my $fpath = $_[1];
my $mboxlen = $_[2];
my $endmark = "\r\n\r\n";
my $wlen;
# emlxからデータ取得
my $rbuf;
open(FH, $fpath) or die "$!\n";
# 1行目の謎の数字はmbox用先頭From行に差し替える必要がある。
# 先頭のFrom行の形式は後述
$rbuf = <FH>;
undef($/);
$rbuf = <FH>;
$/ = "\n";
close(FH);
# 先頭From行の形式は
# ^From: <sender> <date>\r\n$
# の形。時間は最後のReceivedヘッダの時刻にする。
# senderはFrom行のaddress。
$rbuf = &add1stFrom($rbuf);
# mboxに書き出し
$wlen = syswrite($wr, $rbuf, length($rbuf)) or die "$!\n";
# mboxの最後は改行2個
syswrite($wr, $endmark, length($endmark)) or die "$!\n";
return ($wlen+length($endmark));
}
sub debugPrint {
my $ref = $_[0];
my $refarry = $ref->{"lines"};
my @lines = @$refarry;
my $isPrint = 0;
if ( $isPrint ) {
print "######\n";
printf("[name](%s)\n", $ref->{"name"});
printf("[linenum] %d\n", $ref->{"line"});
printf("[id] %s\n", $ref->{"id"});
print "-----\n";
foreach ( @lines ) {
print "$_";
}
}
}
sub add1stFrom {
my $body = $_[0];
my @body = split(/\n/, $body);
# 落とした \nを加える
foreach ( @body ) {
$_ = "$_\n";
}
my ( $line, $ref);
my @hdr= &extractHeader(@body);
my $rcvIsNotOccured = 1;
my $fromAddr = "";
my $receivedTime = "";
my $inProgress = 2;
my $i;
for ( $i = 0; $i < $#hdr+1; $i++ ) {
&debugPrint($hdr[$i]);
}
foreach $ref (@hdr) {
if ( $ref->{"name"} =~ /From/ ) {
$fromAddr = &extractFromAddress($ref);
$inProgress -= 1;
} elsif ( $ref->{"name"} =~ /Received/ ){
if ( $rcvIsNotOccured ) {
$receivedTime = &extractReceivedTime($ref);
$inProgress -= 1;
$rcvIsNotOccured = 0;
}
}
if ( $inProgress == 0 ) { last; }
}
#printf("inProgress: %s\n", $inProgress);
#printf("fromAddr : %s\n", $fromAddr);
#printf("receivedTime : %s\n", $receivedTime);
return "From $fromAddr $receivedTime\r\n$body";
}
sub extractFromAddress {
my $ref = $_[0];
my $rar = $ref->{"lines"};
my @ar = @$rar;
my $line = $ar[0];
$_ = $line;
# mail address regex
# [\w.-]+\@([\w-[\w-]+\.)+\w+
/.*[^\w.-]([\w.-]+\@([\w-[\w-]+\.)+\w+)[^\w].*/;
my $ret = $1;
return $ret;
}
sub extractReceivedTime {
my $ref = $_[0];
my $rar = $ref->{"lines"};
my @ar = @$rar;
my $line;
foreach (@ar) {
s/[\r\n]//g;
$line .= $_;
}
# 日付の形式は 曜日 mm dd HH:MM:SS yyyy
# asctime formatである
my @tmp = split(/;/, $line);
my $date = $tmp[$#tmp];
if ( $date =~ /^\s{1,}(.*)/ ) {
$date = $1;
}
return &myAscTime($date);
}
sub myAscTime {
my $time = $_[0];
# 取得するのはRFC2822にあるFMT
# 曜日, dd mm yyyy HH:MM:SS +TZ(場所)
$_ = $time;
s/,//;
my @tmp = split(/ /,$_);
my $w_day = 0;
my $day = 1;
my $mon = 2;
my $year = 3;
my $time = 4;
$time = "$tmp[$w_day] $tmp[$mon] $tmp[$day] $tmp[$time] $tmp[$year]";
# 日付の形式は 曜日 mm dd HH:MM:SS yyyy
# asctime formatである
return $time;
}
sub mkHeaderLine {
my @hl = @_;
my $ret = {name => "", linenum => 0, lines => "",
id=>""};
# set header lines
$ret->{"lines"} = \@hl;
$ret->{"linenum"} = $#hl;
# parse header name
$ret->{"name"} = (split(/:/, $hl[0]))[0];
return $ret;
}
sub extractHeader {
my @msg = @_;
my @ret = ();
my @hdr;
my ($i, $j, $ref);
my $runInLoop = 0;
my $blockCount = 0;
for ( $i = 0; $i < $#msg +1; $i++ ) {
@hdr = ($msg[$i]);
$j = $i+1;
$runInLoop = 0;
SW_EXTRACT_HDR_BLOCK:{
while ( $msg[$j] =~ /^\s{1,}/ ) {
# headerのデリミタなら脱出
if ( $msg[$j] =~ /^\r\n$/ ) {
last SW_EXTRACT_HDR_BLOCK;
}
push(@hdr, $msg[$j]);
$j += 1;
$runInLoop = 1;
}
};# end of SW_EXTRACT_HDR_BLOCK
$ref = &mkHeaderLine(@hdr);
$ref->{"id"} = $blockCount;
$blockCount += 1;
push(@ret, $ref);
if ( $runInLoop ) {
# 次のループに移るときに $i+=1となるので、1少なく
# 足してみた。びみょー
$i = $j -1;
}
last if ( $msg[$i] =~ /^[\r\n]{1,}$/ );
}
return @ret;
}
sub reportProgress {
my $total = $_[0];
my $finished = $_[1];
my $percent = ($finished/$total)*100;
printf("%3.2f\%(%d/%d) is over\n",
$percent, $finished, $total);
}
sub usage {
printf("usage: $0 targetdir\n");
print <<"EOF";
useage : $0 targetdir [tofile]
targetdir : the driectory that convert to mbox.
tofile : mbox filename converted from emlx dir.
EOF
if ( $#_ > 0 ) {
foreach (@_) {
print "\t$_";
}
}
exit(2);
}
|