root/lang/perl/misc/i18n-emoji/EmailParser.pm

Revision 3817, 29.2 kB (checked in by kawa0117, 11 months ago)

see http://www.kawa.net/works/perl/i18n-emoji/i18n-emoji.html (2005/09/13)

Line 
1# ------------------------------------------------------------------------
2#   EmailParser.pm --- �ޥ��ѡ���б��᡼���߼��⥸�塼��   Copyright 1997-2005 Kawasaki Yusuke <u-suke@kawa.net>
3# ------------------------------------------------------------------------
4    package EmailParser;
5    use strict;
6    use vars qw( $DEBUG $VERSION );
7    $VERSION = "0.01";
8    $DEBUG = \*STDERR;
9# ------------------------------------------------------------------------
10    use MIME::Base64;
11    use Encode;
12    use DateTime::Format::Mail;
13    use IO::File;
14    use IO::Scalar;
15    use IO::ScalarArray;
16# ------------------------------------------------------------------------
17=head1 NAME
18
19    EmailParser.pm ---- Multipart email parser
20
21=head1 USAGE
22
23    use EmailParser;
24
25    $ep = EmailParser->new( $file );
26    # $ep = EmailParser->new()->fromHandle( $handle );
27    # $ep = EmailParser->new()->fromFile( $file );
28    # $ep = EmailParser->new()->fromArray( $array );
29    # $ep = EmailParser->new()->fromString( $scalar );
30
31    $ep->setInternalTimeZone( "Japan" );
32    $ep->setInternalCharset( "utf8" );
33    ( $part0, $part1, $part2, ... ) = $ep->parse();
34
35    $string = $part0->toString();
36    $array  = $part1->toArray();
37    $file   = $part2->toFile( $file );
38    $handle = $part3->toHandle( $handle );
39
40=head1 PARSED PROPATIES
41
42    �ڶ��������    $part->{format} mulitipart/��  multipart content
43                    text/plain      text content
44                    text/html       HTML content
45                    image/��       image data content
46                    message/rfc822  RFC822 content
47    $part->{encoding}   base64      Base64
48                        quoted      quoted-printable
49    $part->{offset}                 offset to this part
50    $part->{boundary}               boundary string of the part
51    $part->{head_text}              raw header text
52    $part->{raw}->{FROM}->[0]           From: line
53    $part->{raw}->{SUBJECT}->[0]        Subject: line
54    $part->{raw}->{CONTENT_TYPE}->[0]   Content-Type: line
55
56    �ڥ᡼������Τߤ�����    $part->{addr_from}->[0]         ���пͥ᡼�륢�ɥ쥹
57    $part->{addr_to}->[0]��        ����᡼�륢�ɥ쥹To:
58    $part->{addr_cc}->[0]��        ����᡼�륢�ɥ쥹Cc:
59    $part->{addr_bcc}->[0]��       ����᡼�륢�ɥ쥹Bcc:
60    $part->{subject}                �᡼��̾
61    $part->{epoch}                  ������(�)
62    $part->{datetime}               ������(YYYY/MM/DD HH:MM:SS)
63    $part->{priority}               ͥ��������    $part->{ml_name}                ML̾��
64    �ڥޥ��ѡ��ȥ������Τߤ�����    $part->{child_boundary}         �ҥѡ��ȤΥХ������    �ڥƥ����ȥ������Τߤ�����    $part->{charset}                �����������
65=head1 HISTORY
66
67    1997/
68    2005/04/21 EmailParser.pm
69    2005/05/08 EmailParser::Part ���饹���
70
71=head1 COPYRIGHT
72
73    Kawasaki Yusuke <u-suke@kawa.net>
74    http://www.kawa.net/
75
76=cut
77# ------------------------------------------------------------------------
78    my $INTERNAL_TIMEZONE = "Japan";    # �ǥե����Υ����ॾ����  my $INTERNAL_CHARSET = "utf8";      # �����ʸ����# ------------------------------------------------------------------------
79#   ���󥹥ȥ饯��
80# ------------------------------------------------------------------------
81sub new {
82    my $pkg = shift;
83    my $file = shift;
84    ## $DEBUG and print $DEBUG "[EmailParser::new] $pkg $file\n";
85    my $emp = {};
86    bless $emp, $pkg;
87    $emp->fromFile( $file ) if $file;
88    $emp;
89}
90# ------------------------------------------------------------------------
91#   �ǥ��󥹥ȥ饯��
92# ------------------------------------------------------------------------
93sub DESTROY {
94    my $emp = shift or return;
95    ## $DEBUG and print $DEBUG "[DESTROY] $emp\n";
96    if ( $emp->{ihandle_opened} ) {
97        # ï¿½ï¿½ï¿½Ï¥ï¿½ë¤¬ï¿½ï¿½ï¿½ï¿½ï¿½×¥ï¿½ß¤Î¾ï¿½ï¿½Ï¥ï¿½ï¿½í¡¼ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½Â       eval {
98            $emp->{ihandle}->close();
99        };
100    }
101    $emp;
102}
103# ------------------------------------------------------------------------
104#   �������ॾ�����ꤹ���᡼�������Υ����ॾ����Ϥʤ���# ------------------------------------------------------------------------
105sub setInternalTimeZone {
106    my $emp = shift;
107    my $timezone = shift;
108    # DateTime::TimeZone ����ȳ���Ǥ��뤫�⡩
109    $emp->{timezone} = $timezone;
110}
111sub getInternalTimeZone {
112    defined $_[0]->{timezone} ? $_[0]->{timezone} : $INTERNAL_TIMEZONE;
113}
114# ------------------------------------------------------------------------
115#   ��ʸ���Ȥ�ꤹ���᡼��ʸ���ȤǤϤʤ���# ------------------------------------------------------------------------
116sub setInternalCharset {
117    my $emp = shift;
118    my $charset = shift;
119    EmailParser::Util::valid_charset( $charset ) or die "Invalid charset: $charset\n";
120    $emp->{charset} = $charset;
121}
122sub getInternalCharset {
123    defined $_[0]->{charset} ? $_[0]->{charset} : $INTERNAL_CHARSET;
124}
125# ------------------------------------------------------------------------
126#   ��߹��ߥϥ����
127# ------------------------------------------------------------------------
128sub fromHandle {
129    my $emp = shift;
130    my $ifh = shift;
131    ## $DEBUG and print $DEBUG "[fromHandle] ",ref($ifh),"\n";
132    $emp->{ihandle} = $ifh;             # ��߹��ߥϥ���Ͽ
133    $emp->{begin} = $emp->tell();       # ���ϰ�֤��
134    $ifh;
135}
136# ------------------------------------------------------------------------
137sub fromFile {
138    my $emp = shift;
139    my $file = shift;
140    ## $DEBUG and print $DEBUG "[fromFile] $file\n";
141    my $ifh = new IO::File( $file, "r" ) or die "$! - $file\n";
142    $emp->{ihandle_opened} ++;          # OPEN�ե饰��    $emp->fromHandle( $ifh );
143}
144# ------------------------------------------------------------------------
145sub fromArray {
146    my $emp = shift;
147    my $array = shift;
148    ## $DEBUG and print $DEBUG "[fromArray] $array\n";
149    my $ifh = new IO::ScalarArray( $array ) or die "IO::ScalarArray failed.\n";
150    $emp->{ihandle_opened} ++;          # OPEN�ե饰��    $emp->fromHandle( $ifh );
151}
152# ------------------------------------------------------------------------
153sub fromString {
154    my $emp = shift;
155    my $string = shift;
156    ## $DEBUG and print $DEBUG "[fromString] \$string\n";
157    my $ifh = new IO::Scalar( \$string ) or die "IO::Scalar failed.\n";
158    $emp->{ihandle_opened} ++;          # OPEN�ե饰��    $emp->fromHandle( $ifh );
159}
160# ------------------------------------------------------------------------
161#   ����ɤ߹��� ------------------------------------------------------------------------
162sub getline {
163    $_[0]->{ihandle}->getline() if $_[0]->{ihandle};
164}
165sub seek {
166    my $emp = shift;
167    my $pos = shift;
168    ## $DEBUG and print $DEBUG "[seek] $pos\n";
169    $emp->{ihandle}->seek($pos,0) if $emp->{ihandle};
170}
171sub tell {
172    my $emp = shift;
173    $emp->{ihandle}->tell() if $emp->{ihandle};
174}
175# ------------------------------------------------------------------------
176#   �Ƭ�˴������
177# ------------------------------------------------------------------------
178sub rewind {
179    my $emp = shift;
180    my $begin = $emp->{begin};
181    ## $DEBUG and print $DEBUG "[rewind] $begin\n";
182    $emp->seek( $begin );
183}
184# ------------------------------------------------------------------------
185#   �᡼���(�ޤ��ϥޥ��ѡ�����������ơ��ƥѡ��Ȥ��
186# ------------------------------------------------------------------------
187sub parse {
188    my $emp = shift;
189    my $selfbnd = shift;                    # ���ѡ��ȤΥХ����    my $epart = EmailParser::Part->new( $emp, $selfbnd );
190    my $childbnd = $epart->{child_boundary};  # �ҥѡ��ȤΥХ����   my $is_multi = ( $childbnd && $epart->{format} =~ m#^multipart/# );
191    my @parts = ();
192    my $findchr = $epart->{charset};
193
194    # ���Ȥ��ޥ��ѡ��Ȥξ��ϡ��ҥѡ��Ȥ�Ϥ���    if ( $is_multi ) {
195        # �ǽ��ҥѡ��Ȥ��Ϥޤ��Ǥ����
196        while( 1 ) {
197            my $line = $emp->getline();
198            last unless defined $line;
199            $line =~ s/[\r\n]*$/\n/s;
200            last if ( $line =~ /^--\Q$childbnd\E[\r\n]*$/ );
201        }
202        # �ƻҥѡ��Ȥ򷫤����ʺƵ��ƤӽФ���        my $partcnt;
203        while ( 1 ) {
204            $partcnt ++;
205            ## $DEBUG and print $DEBUG "[parse] multipart #$partcnt\n";
206            my @childlen = $emp->parse( $childbnd );
207            my $partcnt = scalar @childlen or last; # �ޥ��ѡ��Ƚ�λ
208            push( @parts, @childlen );              # ����¤٤Ƥ���
209            my $lastchild = $childlen[$#childlen] or last;
210            last unless $lastchild->{has_next};     # ³���Υѡ��Ȥ�����       }
211        ## $DEBUG and print $DEBUG "[parse] multipart finish\n";
212
213        # �Ƥ�����Ȥ���������Ҥ�����Ȥ�Ƚ�������
214        # �Ҥ�����Ȥκǽ�ʸ���Ȥ򡢿Ƥ�����ȤȤߤʤ�
215        if ( ! $findchr && scalar @parts ) {
216            $findchr = ( map {$_->{charset}}
217                         grep {defined $_->{charset}} @parts )[0];
218            ## $DEBUG and print $DEBUG "[parse] child charset=$findchr\n";
219        }
220    }
221
222    # �ޥ��ѡ��ȤǤʤ���拾���ξ��ϡ��ʸ�����ɤ���
223    # ������ʤ��顢�⤷����ʤ�����������⤹��    if ( ! $is_multi ) {
224        my $notascii = ( $findchr ne "" );                          #
225        my $is_text = ( $epart->{format} =~ m#^text/# );            #
226        my $enc_qp = ( $epart->{encoding} eq "quoted-printable" );
227        my $enc_b64 = ( $epart->{encoding} eq "base64" );
228        my $prevtag;
229
230        while( 1 ) {
231            my $line = $emp->getline();
232            last unless defined $line;
233            if ( $selfbnd && $line =~ /^--\Q$selfbnd\E(--)?[\r\n]*$/ ) {
234                $epart->{has_next} ++ unless $1;    # ³���Υѡ��Ȥ�����               last;
235            }
236            next if ( $line =~ /^$/ );
237
238            if ( ! $findchr && $is_text ) {
239                if ( $enc_qp ) {
240                    $line =~ s/=([a-z0-9]{2})/pack(C=>hex($1))/gei;
241                } elsif ( $enc_b64 ) {
242                    $line =~ s/\s+$//s;
243                    next unless ( length($line) % 4 == 0 );     # 4��ܿ�ʤ���̵��                   $line = MIME::Base64::decode_base64($line);
244                }
245                $line = $prevtag.$line if $prevtag;
246                if ( $line =~ m#(?:^|\W)charset=["']?([\w\-\.]+)#i ) {
247                    ## $DEBUG and print $DEBUG "[parse] html charset=$1\n";
248                    $findchr = EmailParser::Util::valid_charset( $1 );
249                } elsif ( $line =~ m#\e\$B# ) {
250                    $findchr = "ISO-2022-JP";                   # ���������ץ�������                } elsif ( $line =~ m#https?://[\w\%\-\.]+\.(\w{2})/#i ) {
251                    my $cntry = $1;
252                    ## $DEBUG and print $DEBUG "[parse] country=$cntry\n";
253                    $findchr = $EmailParser::COUNTRY2CHARSET->{$cntry};
254                }
255                $notascii ++ if ( ! $notascii && $line =~ /[^\000-\177]/ );
256                $prevtag = ( $line =~ /(<[^<>]*)$/s )[0];       # �Ǹ�����
257            }
258        }
259
260        # charset ���ǡ����ġ�� ASCII ʸ�����������ʤ��� us-ascii
261        if ( ! $findchr && $is_text && ! $notascii  ) {
262            $findchr = "us-ascii";
263            ## $DEBUG and print $DEBUG "[parse] findchr=$findchr\n";
264        }
265    }
266
267    # �ʸ�������ɤ����ޤ����ʤ顢MIME�إå���
268    if ( ! $findchr ) {
269        if ( $epart->{head_text} =~ /(\=\?([\w\-]+)\?[BQ]\?)([^\s\?]+)\?\=/i ) {
270            $findchr = EmailParser::Util::valid_charset( $2 );
271        } elsif ( $epart->{head_text} =~ /\e\$B/ ) {
272            $findchr = "ISO-2022-JP";
273        }
274    }
275
276    # ʸ���ɤ����ޤ����ʤ顢���пͥ᡼�륢�ɥ쥹���
277    if ( ! $findchr && ref $epart->{raw}->{FROM} ) {
278        my $from = $epart->{raw}->{FROM}->[0];                      # ���пͥ��ɥ쥹
279        my $DOMAIN2CHARSET = EmailParser::Map::domain2charset();
280        my $domain = ( $from =~ /\@(?:[\w\-]+\.)*([\w\-]+\.[\w\-]+)(\W|$)/ )[0];         # �ɥᥤ��        $findchr ||= $DOMAIN2CHARSET->{lc($domain)} if $domain;
281        $DEBUG and print $DEBUG "[parse] domain=$domain [$findchr]\n";
282
283        my $COUNTRY2CHARSET = EmailParser::Map::country2charset();
284        my $cntry = ( $from =~ /\@[\w\-\.]+\.(\w{2})(\W|$)/)[0];    # ��ɥᥤ��      $findchr ||= $COUNTRY2CHARSET->{lc($cntry)} if $cntry;
285        $DEBUG and print $DEBUG "[parse] country=$cntry [$findchr]\n";
286    }
287
288    # ʸ���ɤ򺣲󸡽Ф��Ƥ����ʤ顢�إå�������Ƥ���
289    if ( ! $epart->{charset} && $findchr ) {
290        $epart->{charset} = $findchr;
291        my $convto = $emp->getInternalCharset();
292        $epart->convertCharset( $convto ) if $convto;
293    }
294
295    # ������������ѡ��Ȥ�������
296    unshift( @parts, $epart );
297
298    # ��λ
299    wantarray ? @parts : $epart;
300}
301# ------------------------------------------------------------------------
302    package EmailParser::Part;
303    use strict;
304    use vars qw( $DEBUG );
305    $DEBUG = \*STDERR;
306# ------------------------------------------------------------------------
307sub new {
308    my $pkg = shift;
309    my $emp = shift or return;  # MailParser object
310    my $selfbnd = shift;
311    ## $DEBUG and print $DEBUG "[EmailParser::Part::new] $pkg $emp $selfbnd\n";
312    my $epart = {};
313    bless $epart, $pkg;
314    $epart->{email} = $emp;
315    $epart->{offset} = $emp->tell();
316    $epart->{boundary} = $selfbnd;
317    $epart->readHeader();
318    $epart;
319}
320# ------------------------------------------------------------------------
321#   �إå��������� ------------------------------------------------------------------------
322sub readHeader {
323    my $epart = shift;
324
325    my $emp = $epart->{email};
326    my $selfbnd = $epart->{boundary};
327
328    # �������ե��åȤ˰������    my $offset = $epart->{offset};
329    $emp->seek( $offset ) if defined $offset;
330
331    # �إå��߹���    my $harray = [];
332    while( 1 ) {
333        my $line = $emp->getline();
334        last unless defined $line;
335        last if ( $line =~ /^$/ );      # �إå��λ
336        if ( $selfbnd && $line =~ /^--\Q$selfbnd\E(--)?[\r\n]$/ ){
337            return;                     # �ѡ��Ƚ�λ���å�ʤ�
338        }
339        $line =~ s/[\r\n]+$//s;
340        push( @$harray, $line );
341    }
342    $epart->{head_text} = join( "", @$harray );
343
344    # �إå��å���Ÿ������    my $raw = {};
345    my $key;
346    foreach ( @$harray ) {
347        if ( /^([\w\-]+)\:\s*(.*)$/ ) {
348            $key = uc($1);
349            my $val = $2;
350            $key =~ s/\W/_/g;           
351            $raw->{$key} ||= [];
352            push( @{$raw->{$key}}, $val );
353        } elsif ( $key ne "" && /^\s+(.*)/ ) {
354            $raw->{$key}->[$#{$raw->{$key}}] .= $1; # ����ɲä��Ƥ���
355        } else {
356            $key = "";
357        }
358    }
359    $epart->{raw} = $raw;
360
361    # ʸ���ɤβ�
362    my $contype;
363    $contype = $raw->{CONTENT_TYPE}->[0] if ref $raw->{CONTENT_TYPE};
364    $contype =~ s/\s+/ /sg;
365    my $charset;
366    if ( $contype =~ /\;\s*charset=["']?([\w\-\.]+)["']?/i ) {
367        ## $DEBUG and print $DEBUG "[readHeader] charset=$1\n";
368        $charset = EmailParser::Util::valid_charset( $1 );
369    }
370    $epart->{charset} = $charset;
371
372    # �⤷ charset= ������ɤ�ʬ���äƤ�����������Ƥ���
373
374    my $convto = $emp->getInternalCharset();
375    $epart->convertCharset( $convto ) if $convto;
376
377    # Content-Type: �Ԥβ�
378    $contype = $raw->{CONTENT_TYPE}->[0] if ref $raw->{CONTENT_TYPE};
379    $contype =~ s/\s+/ /sg;
380    my $format = ( $contype =~ m#^([\w\-\.]+/[\w\-\.]+)# )[0];
381    $format =~ tr/A-Z/a-z/;
382    $epart->{format} = $format;
383    ## $DEBUG and print $DEBUG "[readHeader] format=$format\n";
384
385    # �ҥѡ��ȤΥХ����    my $childbnd = ( $contype =~ /\;\s*boundary=["']?([^\s"']+)/i )[0];
386    $epart->{child_boundary} = $childbnd;
387    ## $DEBUG and print $DEBUG "[readHeader] child_boundary=$childbnd\n";
388
389    # Content-Transfer-Encoding: �Ԥβ�base64 quoted-pritable
390
391    my $encoding = ( $raw->{CONTENT_TRANSFER_ENCODING}->[0] =~ /^([\w\-]+)/ )[0]
392        if ref $raw->{CONTENT_TRANSFER_ENCODING};
393    $encoding =~ tr/A-Z/a-z/;
394    $epart->{encoding} = $encoding;
395    ## $DEBUG and print $DEBUG "[readHeader] encoding=$encoding\n";
396
397    # Date: �Ԥβ�
398    my $srcdate;
399    $srcdate = $raw->{DATE}->[0] if ref $raw->{DATE};
400    $srcdate =~ s/[^\040-\176]+/ /g;            # ���߽�  $srcdate ||= ( $raw->{RECEIVED}->[$#{$raw->{RECEIVED}}] =~ /;\s*
401                (\d+\s+[a-z]+\s+\d+\s+\d+:\d+:\d+\s+[\+\-]\d+(\:\d+)?)
402                (\D|$)/xi )[0] if ref $raw->{RECEIVED};
403    ## $DEBUG and print $DEBUG "[readHeader] Date: $srcdate\n";
404    if ( $DateTime::Format::Mail::VERSION && $srcdate ) {
405        my $dt;
406        eval {
407            my $dtfm = DateTime::Format::Mail->new()->loose();
408            $dt = $dtfm->parse_datetime( $srcdate );
409        };
410        if ( ref $dt ) {
411            my $tz = $emp->getInternalTimeZone();       # �����������ॾ����          $dt->set_time_zone( $tz ) if $tz;   # �����ॾ�����          $epart->{epoch} = $dt->epoch();
412            my $datetime = $dt->ymd('/')." ".$dt->hms(':');
413            $epart->{datetime} = $datetime;
414            ## $DEBUG and print $DEBUG "[readHeader] datetime=$datetime\n";
415        }
416    }
417
418    # �ե��������ե�����ĥ�Ҥ򸡽�    {
419        my $filename = ( $contype =~ /\;\s*name=["']?([^\s"']+)/i )[0];
420        $filename ||= ( $raw->{CONTENT_DISPOSITION}->[0] =~
421            /\;\s*filename=["']?([^\s"']+)/i )[0]
422            if ref $raw->{CONTENT_DISPOSITION};
423        ## $DEBUG and print $DEBUG "[readHeader] filename=$filename\n";
424        $epart->{filename} = $filename;
425
426        my $ext = ( $filename =~ m#[^\/\:\.]\.(\w[\w\-]*)$# )[0];
427        my $FORMAT2EXT = EmailParser::Map::format2ext();
428        $ext ||= $FORMAT2EXT->{$format} if $format;
429        ## $DEBUG and print $DEBUG "[readHeader] ext=$ext\n";
430        $epart->{ext} = $ext;
431    }
432
433    # Subject: �Ԥ��    {
434        my $subject;
435        $subject = $raw->{SUBJECT}->[0] if ref $raw->{SUBJECT};
436        ## $DEBUG and print $DEBUG "[readHeader] subject=$subject\n";
437        $epart->{subject} = $subject;
438    }
439
440    # X-Priority: �Ԥ��    {
441        my $priority;
442        $priority = $raw->{PRIORITY}->[0] if ref $raw->{PRIORITY};
443        $priority ||= $raw->{X_PRIORITY}->[0] if ref $raw->{X_PRIORITY};
444        $priority = ( $priority =~ /(\d+)/ )[0];
445        ## $DEBUG and print $DEBUG "[readHeader] priority=$priority\n";
446        $epart->{priority} = $priority;
447    }
448
449    # X-ML-Name: �Ԥ��    {
450        my $mlname;
451        $mlname = $raw->{X_ML_NAME}->[0] if ref $raw->{X_ML_NAME};
452        $mlname ||= $raw->{X_SEQUENCE}->[0] if ref $raw->{X_SEQUENCE};
453        $mlname =~ s/\s.*$//s;
454        ## $DEBUG and print $DEBUG "[readHeader] ml_name=$mlname\n";
455        $epart->{ml_name} = $mlname if $mlname;
456    }
457
458    # �إå�����ɥ쥹�ϤιԤ��    my $addrkey = {
459        "FROM"  =>  "addr_from",
460        "TO"    =>  "addr_to",
461        "CC"    =>  "addr_cc",
462        "BCC"   =>  "addr_bcc",
463        "REPLY_TO"      =>  "addr_reply",       # Reply-To:
464        "RETURN_PATH"   =>  "addr_return",      # Return-Path:
465        "DELIVERED_TO"  =>  "addr_deliv",       # Delivered-To: (qmail)
466        "MESSAGE_ID"    =>  "message_id",       # Message-Id:
467        "REFERENCES"    =>  "mess_refer",       # References:
468        "IN_REPLY_TO"   =>  "mess_reply",       # In-Reply-To:
469    };
470    foreach my $ikey ( keys %$addrkey ) {
471        next unless ref $raw->{$ikey};
472        my $okey = $addrkey->{$ikey};
473        my $list = [];
474        foreach my $line ( @{$raw->{$ikey}} ) {
475            my @pickup = EmailParser::Util::pickup_address( $line );
476            push( @$list, @pickup ) if scalar @pickup;
477        }
478        $epart->{$okey} = $list if scalar $list;
479    }
480
481    # �إå���λ
482    $epart;
483}
484# ------------------------------------------------------------------------
485#   �ѡ��Ȥ��Ȥ˥ե������ؽ�Ϥ��� ------------------------------------------------------------------------
486sub toFile {
487    my $epart = shift;
488    my $file = shift;
489    ## $DEBUG and print $DEBUG "[toFile] $file\n";
490    my $ofh = new IO::File( $file, "w" ) or die "$! - $file\n";
491    $epart->toHandle( $ofh, @_ );
492    $ofh->close();
493    $file;
494}
495# ------------------------------------------------------------------------
496sub toArray {
497    my $epart = shift;
498    ## $DEBUG and print $DEBUG "[toArray]\n";
499    my $array = [];
500    my $ofh = new IO::ScalarArray( $array ) or die "IO::ScalarArray failed.\n";
501    $epart->toHandle( $ofh, @_ );
502    $ofh->close();
503    $array;
504}
505# ------------------------------------------------------------------------
506sub toString {
507    my $epart = shift;
508    # $DEBUG and print $DEBUG "[toString]\n";
509    my $string = "";
510    my $ofh = new IO::Scalar( \$string ) or die "IO::Scalar failed.\n";
511    $epart->toHandle( $ofh, @_ );
512    $ofh->close();
513    $string;
514}
515# ------------------------------------------------------------------------
516sub toHandle {
517    my $epart = shift;
518    my $ofh = shift;        # �������   my $ocode = shift;      # ��Ϥ�������    # $DEBUG and print $DEBUG "[toHandle] ",ref($ofh)," $ocode\n";
519
520    # ������ʸ���ɡʥƥ����ȥѡ��Ȥ��Ľ������ɻ���Τ�Ѵ���    my $icode;
521    if ( $Encode::VERSION && $ocode && $epart->{type} =~ m#^text/#i ) {
522        # $DEBUG and print $DEBUG "[toHandle] icode=$icode ocode=$ocode\n";
523        $icode = $epart->{charset};
524    }
525
526    # �Х��������
527    my $selfbnd = $epart->{boundary};           # ���ѡ��ȤΥХ����   my $childbnd = $epart->{child_boundary};    # �ҥѡ��ȤΥХ����   # $DEBUG and print $DEBUG "[toHandle] self_boundary=$selfbnd\n";
528    # $DEBUG and print $DEBUG "[toHandle] child_boundary=$childbnd\n";
529
530    # �������ե��åȤ˰������    my $emp = $epart->{email};
531    my $offset = $epart->{offset};
532    $emp->seek( $offset ) if defined $offset;
533
534    # �إå�����
535
536    while( 1 ) {
537        my $line = $emp->getline();
538        last unless defined $line;
539        last if ( $line =~ /^$/ );      # �إå��λ
540        return if ( $selfbnd && $line =~ /^--\Q$selfbnd\E(--)?[\r\n]$/ );
541    }
542
543    # �ޥ��ѡ��ȷ���ʤ鿿
544    my $is_multi = ( $childbnd && $epart->{format} =~ m#^multipart/# );
545
546    # ���󥳡��ǥ���    my $enc64 = ( $epart->{encoding} eq "base64" );
547    my $encQT = ( $epart->{encoding} eq "quoted-printable" );
548
549    # �ܥǥ���ƥϥ����Ϥ���   my $b64rest;
550    my $multi_started;
551    while( 1 ) {
552        my $line = $emp->getline();
553        last unless defined $line;
554        next if $multi_started;
555        if ( $is_multi && $line =~ /^--\Q$childbnd\E(--)?[\r\n]*$/ ) {
556            $multi_started ++;
557            next;
558        }
559        if ( $selfbnd && $line =~ /^--\Q$selfbnd\E(--)?[\r\n]*$/ ) {
560            $epart->{has_next} ++ unless $1;    # ³���Υѡ��Ȥ�����           last;
561        }
562        $line =~ s/[\r\n]*$/\n/s;
563        if ( $enc64 ) {
564            chomp $line;
565            if ( $b64rest ne "" ) {
566                $line = $b64rest.$line;
567                $b64rest = undef;
568            }
569            my $b64len = length($line);
570            ## $DEBUG and print $DEBUG "[toHandle] base64_length=$b64len\n";
571            if ( $b64len % 4 == 0 ) {         # 4��ܿ���
572                $line = MIME::Base64::decode_base64($line);
573            } else {
574                $b64rest = $line;                   # ������
575                next;
576            }
577        } elsif ( $encQT ) {
578            $line =~ s/=[\r\n]*$//s;
579            $line =~ s/=([0-9a-f]{2})/pack(C=>hex($1))/gei;
580        }
581        Encode::from_to( $line, $icode, $ocode ) if $icode;
582        $ofh->print( $line );
583    }
584    if ( $b64rest ne "" ) {
585        my $line = MIME::Base64::decode_base64($b64rest);
586        Encode::from_to( $line, $icode, $ocode ) if $icode;
587        $ofh->print( $line );    # �Ĥ꤬���ä�
588    }
589
590    $ofh;
591}
592# ------------------------------------------------------------------------
593#   �ѡ��������إå�������ɤ������   �ʸ������ɤ�Ѵ����ޤ���------------------------------------------------------------------------
594sub convertCharset {
595    my $epart = shift;
596    my $convto = shift or return;               # ����ʸ����    ## $DEBUG and print $DEBUG "[convertCharset] convto=$convto\n";
597    return unless $Encode::VERSION;
598
599    my $convfrom = $epart->{current_charset} || $epart->{charset};
600    ## $DEBUG and print $DEBUG "[convertCharset] convfrom=$convfrom\n";
601    return unless $convfrom;                    # ���ߤ�����ɤ���
602
603#   return if ( lc($convto) eq lc($convfrom) ); # ���������ɤǤ�������    # $epart->{XXXX} �γ�ͤ�����ɤ������    foreach my $key ( keys %$epart ) {
604        next if ref $epart->{$key};             # �ϥå��塦���Ͻ�      Encode::from_to( $epart->{$key}, $convfrom, $convto );
605        EmailParser::Util::decode_mime_head( $convto, $epart->{$key} );
606        $epart->{$key} =~ s/[\000-\040\177]/ /gs;
607    }
608
609    # $epart->{raw}->{XXXX} �γ�ͤ�����ɤ������    foreach my $key ( keys %{$epart->{raw}} ) {
610        foreach my $val ( @{$epart->{raw}->{$key}} ) {
611            Encode::from_to( $val, $convfrom, $convto );
612            EmailParser::Util::decode_mime_head( $convto, $val );
613            $val =~ s/[\000-\040\177]/ /gs;
614        }
615    }
616
617    $epart->{current_charset} = $convto;
618}
619# ------------------------------------------------------------------------
620    package EmailParser::Util;
621    use strict;
622    use vars qw( $DEBUG );
623    $DEBUG = \*STDERR;
624# ------------------------------------------------------------------------
625#   ʸ���ɤλ�꤬�������������� ------------------------------------------------------------------------
626sub valid_charset {
627    my $charset = shift or return;
628
629    # �̾���
630    my $CHARSET_ALIAS = {
631        "sjis"  =>  "Shift_JIS",
632        "euc"   =>  "EUC-JP",
633        "jis"   =>  "ISO-2022-JP",
634        "utf-8" =>  "utf8",
635    };
636    if ( defined $CHARSET_ALIAS->{lc($charset)} ) {
637        $charset = $CHARSET_ALIAS->{lc($charset)};
638    }
639
640    # Encode ��������ʾ��ϡ���������ɤ��������    if ( defined $Encode::VERSION ) {
641        $charset = Encode::resolve_alias($charset) or return undef;
642    }
643
644    $charset;   # �����ɳ����λ
645}
646# ------------------------------------------------------------------------
647#   �إå�����ɤ�ѹ�
648# ------------------------------------------------------------------------
649sub decode_mime_head {
650    my $convto = shift or return;
651    return unless defined $Encode::VERSION;
652    foreach ( @_ ) {
653        s{
654            \=\?([\w\-]+)\?([BQ])\?([^\?]+?)\?\=
655        }{
656            my( $charset, $method, $str ) = ( $1, $2, $3 );
657            my $convfrom = EmailParser::Util::valid_charset($charset);
658            if ( $convfrom ) {
659                if ( $method eq "Q" ) {
660                    $str =~ s/=([a-z0-9]{2})/pack(C=>hex($1))/gei;
661                } else {
662                    $str = MIME::Base64::decode_base64($str);
663                }
664                if ( lc($convfrom) ne lc($convto) ) {
665                    Encode::from_to( $str, $convfrom, $convto );
666                }
667            }
668            $str;
669        }iegx;
670    }
671}
672# ------------------------------------------------------------------------
673#   �᡼�륢�ɥ쥹��������� ------------------------------------------------------------------------
674sub pickup_address {
675    my $copy = "" . $_[0];
676    my $list = [];
677    while ( $copy =~ /\@/ ) {
678        # �᡼�륢�ɥ쥹�κ�¦�Υ����Ȥ򳰤�
679        $copy =~ s#^( \s*"([^"]|\")*"\s* | [^"][^\@]*\s+ )##xs;
680        # �᡼�륢�ɥ쥹��Ф���       if ( $copy =~ s#(
681            (?: " [\x21\x23-\x26\x2a\x2b\x2d-\x3b\x3d\x3f\x41-\x7e]+ " |
682                  [\x21\x23-\x26\x2a\x2b\x2d-\x3b\x3d\x3f\x41-\x7e]+ ) \@
683            (?: (?:[\w\-]+\.)+\w+ | \[\d+\.\d+\.\d+\.\d+\] ))##xs ) {
684            push( @$list, $1 );
685        } else {
686            last;
687        }
688        # �᡼�륢�ɥ쥹�α�¦�Υ����Ȥ򳰤�
689        $copy =~ s#^ [\<\>\s]* \(.*?\) \s* ##xs;
690        $copy =~ s#^ [^,\@]* , \s* ##xs;
691    }
692    return unless scalar @$list;
693    @$list;
694}
695# ------------------------------------------------------------------------
696    package EmailParser::Map;
697    use strict;
698# ------------------------------------------------------------------------
699#   �ɥᥤ�󤴤ȤΥǥե����Υ᡼�����ȡʼ�SPAM��# ------------------------------------------------------------------------
700sub domain2charset {
701    my $domain2charset = {
702        "hanmail.net"   =>  "EUC-KR",
703        "korea.com"     =>  "EUC-KR",
704        "sinamail.com"  =>  "Big5",
705        "hinet.net"     =>  "Big5",
706    };
707}
708# ------------------------------------------------------------------------
709#   �񤴤ȤΥǥե����Υ᡼������# ------------------------------------------------------------------------
710sub country2charset {
711    my $country2charset = {
712        cn  =>  "GB2312",       # ���      hk  =>  "Big5",         # ��
713        tw  =>  "Big5",         # ��
714        kr  =>  "EUC-KR",       # �ڹ�      ru  =>  "koi8-r",       # ï¿½í¥·ï¿½ï¿½
715        us  =>  "iso-8859-1",   # ������
716    };
717}
718# ------------------------------------------------------------------------
719#   ���������פȳ���Ҥ�б�
720# ------------------------------------------------------------------------
721sub format2ext {
722    my $format2ext = {
723        "text/plain"    =>  "txt",
724        "text/html"     =>  "html",
725        "image/jpeg"    =>  "jpg",
726        "image/gif"     =>  "gif",
727        "application/vnd.ms-excel"  =>  "xls",
728    };
729}
730# ------------------------------------------------------------------------
731;1;
732# ------------------------------------------------------------------------
Note: See TracBrowser for help on using the browser.