# 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