# vim: set cindent expandtab ts=4 sw=4:
#
# Copyright (c) 1998-2005 Chi-Keung Ho. All rights reserved.
#
# This programe is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# Extmail - a high-performance webmail to maildir
# $Id$
package Ext::RFC822;

use Exporter;
use POSIX qw(strftime);
use MIME::Base64;
use MIME::QuotedPrint;

use vars qw(@ISA @month_map @EXPORT_OK @EXPORT);
@ISA = qw(Exporter);
@EXPORT_OK = qw(str2time);
@EXPORT = qw(rfc822_date rfc822_encode_str rfc822_encode_addr
            date_fmt rfc822_addr_parse);

%month_map = (
    Jan => "01",
    Feb => "02",
    Mar => "03",
    Apr => "04",
    May => "05",
    Jun => "06",
    Jul => "07",
    Aug => "08",
    Sep => "09",
    Oct => "10",
    Nov => "11",
    Dec => "12"
);

sub str2time {
    #eval { require Ext::DateTime };
    #unless ($@) {
    #    Ext::DateTime->import(qw(datefield2dateserial));
    #    return datefield2dateserial($_[0]);
    #}

    my $s = $_[0]; # RFC822 time format
    my @a = split (/\s+/, $s); # \s+ can handler more space

    return 0 if(scalar @a < 5 || $a[3]=~/[^\d]/ ||
        $a[1]=~/[^\d]/ || $a[4]=~/[^:\d]/ ||
        !$month_map{$a[2]});
    $a[3]+=1900 if($a[3]>=70 && $a[3]<100);
    $a[3]+=2000 if($a[3]<70);
    $a[1]='0'.$a[1] if(length($a[1])<2);

    # $a[4] =~s/://g; # convert 12:34:56 => 123456
    $a[4] = hms_fmt($a[4]); # convert 12:34:56 => 123456
    # year+mon+day+time
    my $str = "$a[3]".$month_map{$a[2]}."$a[1]$a[4]";
    return $str;
}

# format hour/min/second to standard xx:yy:zz
sub hms_fmt {
    my $s = $_[0]; # xx::yy::zz
    my @a = split(/:/, $s);
    my $len = scalar @a;
    if($len == 3) {#
        $a[0]="0$a[0]" if(length($a[0])<2);
        $a[1]="0$a[1]" if(length($a[1])<2);
        $a[2]="0$a[2]" if(length($a[2])<2);
    }elsif($len == 2) {
        # a hour/min/sec string without sec part:(
        $a[0]="0$a[0]" if(length($a[0])<2);
        $a[1]="0$a[1]" if(length($a[1])<2);
        push @a, "00";
    }elsif($len == 1) {
        $a[0]="0$a[0]" if(length($a[0])<2);
        push @a, "0000";
    }
    return "$a[0]$a[1]$a[2]";
}

sub date_fmt {
    my $fmt = shift; # format, eg: %s/%s
    my $date = shift; # RFC822 format
    my @a = split (/\s+/, $date); # \s+ can handler more space

    return $date if(scalar @a < 5 || $a[3]=~/[^\d]/ ||
        $a[1]=~/[^\d]/ || $a[4]=~/[^:\d]/);

    my @t = split(/:/, $a[4]);
    $a[1]='0'.$a[1] if(length($a[1])<2);

    # return format: Month day hour min
    return sprintf("$fmt", $a[2],$a[1],$t[0],$t[1]);
}

sub rfc822_date {
    my ($timezone) = $_[0] || '+0800';
    # XXX FIXME currently not support zone, ouch :-(
    return (strftime "%a, %d %b %Y %H:%M:%S $timezone", localtime);
}

sub rfc822_encode_str {
    my ($charset, $str) = @_;
    my @m = split("\n", encode_base64($str));
    my $buf = "";
    foreach my $id (0...scalar @m-1) {
        # append a white space on the following line
        $buf .= ($id eq 0?"": ' 'x 4)."=?$charset?B?".$m[$id]."?=";
        $buf .= ($id eq scalar @m-1 ? "" : "\n");
    }
    $buf;
}

sub rfc822_encode_addr {
    my $name = '[^\'"]*'; # * match 0 or more times, compatible with
                          # some addr that without name part
    my $addr = '[a-z0-9A-Z\-_\.=]+@[a-z0-9A-Z-\_.]+';
    my ($charset, $str) = @_;

    $str=~s/(\r|)\n//g; # remove all CRLF
    my @m = split(/\s*,\s*/, $str);
    my $buf = "";
    foreach my $id (0...scalar @m -1) {
        $m[$id]=~s/^\s+//;
        $m[$id]=~s/\s+$//;
        next unless ($m[$id]);

        # if match continue, but if not match, next loop, if we continue
        # without any match, $1 or $3 will keep the old value, sucks!
        $m[$id] =~ m#\s*['"]*\s*($name)\s*['"]*(\s+|^)<*($addr)>*# or next;

        if(!$1) {
            next unless $3; # ignore those without addr part
            $buf .= ($id eq 0? '': ' 'x 4)."$3,\n"; # insert white space except
                                                    # first line
            next;
        }
        $buf .= ($id eq 0? '"':' 'x 4 .'"'); # insert white space except
                                             # first line
        $buf .= rfc822_encode_str($charset, $1).'" <'.$3.'>';
        $buf .= ",\n"; # always add the suffix
    }
    $buf=~s/\n{2,}/\n/sg; # bug fix and remove redundunt crlf
    $buf=~s/,\n$//; # remove the last suffix
    $buf;
}

sub rfc822_addr_parse {
    my $s = $_[0];
    my $ref = {};
    my $name = '[^\'"<>]*';# match 0 or more times, compatible with
                           # some addr that without name part
    my $addr = '[a-z0-9A-Z\-_\.=]+@[a-z0-9A-Z-\_.]+';
    $s =~ s/[\r\n]+//g;
    $s =~ s/^\s+//;
    $s =~ s/\s+$//;

    if ($s =~ m#^['"]#) {
        $s =~ m#^['"]\s*($name)\s*['"]\s*<*($addr)*>*#;
        if ($1) {
            $ref = { name => $1, addr => $2 }
        } else {
            my $mail = $2;
            $mail =~ /^([^\@]+)@/;
            $ref = { name => $1 ? $1 : $mail, addr => $mail};
        }
    }elsif ($s =~ m#[<>]#) {
        $s =~ m#([^<>]*)<($addr)*>#;
        if ($1) {
            $ref = { name => $1 ? $1 : $2, addr => $2 }
        } else {
            my $mail = $2;
            $mail =~ /^([^\@]+)@/;
            $ref = { name => $1 ? $1 : $mail, addr => $mail};
        }
    } else {
        if ($s) {
            # there is a mysterious bug here, if $s is '', then
            # after regexp excution, $1 will set to 'S', FIXME XXX
            $s =~ /([^\@\s]+)@/;
            $ref = { name => $1 ? $1 : $s, addr => $s };
        }
    }
    $ref;
}

1;


syntax highlighted by Code2HTML, v. 0.9.1