#! /usr/bin/perl -w
# -*- Perl -*-
#
# mailutime - a tool to unify a file time stamp with 
#             RFC 822 'Date:' field in a mail.
#             It also supports MHonArc messages.
# $Id: mailutime.in,v 1.3.4.6 2006-11-09 15:03:50 opengl2772 Exp $
#
# Version 1.0.3    [01/16/2004]
# Version 1.0.2    [12/12/1998]
#
# USAGE:
#
#   usage: mailutime <Target Files>...
#
#
# RECOMMENDED USAGE:
#
#   % find . -type f ~/Mail/ml/foo |xargs mailutime
#
#   It is very useful way to modify time stamps for all files in a
#   specified mailbox (MH format).
#
# Copyright (C) 1998-1999 Satoru Takabayashi  All rights reserved.
# Copyright (C) 2002-2006 Namazu Project All rights reserved.
#     This is free software with ABSOLUTELY NO WARRANTY.
#
#  This program 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 versions 2, or (at your option)
#  any later version.
# 
#  This program is distributed in the hope that it will be useful
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#  02111-1307, USA

require 5.004;
use strict;
use IO::File;
use Time::Local;

my $PKGDATADIR = $ENV{'pkgdatadir'} || "/usr/share/namazu";
push(@INC, $PKGDATADIR . "/pl");
require 'time.pl';

$| = 1;   # autoflush output 

my %month_names = ("Jan" => 0, "Feb" => 1, "Mar" => 2, "Apr" => 3, 
		   "May" => 4, "Jun" => 5, "Jul" => 6, "Aug" => 7, 
		   "Sep" => 8, "Oct" => 9, "Nov" => 10, "Dec" => 11);
my $re_month =  join '|', keys %month_names;
my $re_day   =  '(?:0?[1-9]|[12][0-9]|3[01])';
my $re_year  =  '(?:\d\d\d\d|\d\d)';  # allow 2 digit fomrat
my $re_hour  =  '(?:[01][0-9]|2[0-3])';
my $re_min   =  '(?:[0-5][0-9])';
my $re_sec   =  '(?:[0-5][0-9])';

my $DebugOpt = 0;
    
sub main();

main();

sub main () {
    foreach my $file (@ARGV) {
	my $date;
	my ($fh) = new IO::File;
	$fh->open("$file") || die "$!: $file\n";
	while (<$fh>) {
	    last if /^$/;
	    $date = $1 if /^(?:<!--X-)?Date: (.*)/i;
	}
	unless (defined($date)) {
	    print STDERR "$file: 'Date:' not found!\n";
	    next;
	}
	$fh->close;

	my $time = time::rfc822time_to_mtime($date);
	if ($time == -1) {
	    print STDERR "Warning! $file: [$date] is not rfc822 format! \n";
	    print STDERR "\t\t\t\ttrying fuzzy mode...\n";
	    $time = get_date_fuzzily($date);
	}
	if ($time != -1) {
	    utime($time, $time, $file);
	    print "Complete! $file: $date -> $time\n";
	} else {
	    print STDERR "$file: [$date] malformed 'Date:' format! \n";
	    next;
	}
    }
}

# calculate time from the RFC 822 'Date: ' field string like:
#   'Thu, 18 Dec 1997 21:09:43 GMT'
# a timezone and its adjustment such as GMT or +0900 would be ignored.
sub rfc822time($) {
    my ($date) = @_;

    if ($date =~ /
	^\s*
	(\w{3},\s+|)                       # a day of the week (ignored)
	($re_day)\s+                       # a day of the month
	($re_month)\s+                     # name of month
	($re_year)\s+                      # year
	($re_hour):($re_min):($re_sec)     # HH:MM:SS
	/x) 
    {
	my ($mday, $mon, $year, $hour, $min, $sec) = ($2, $3, $4, $5, $6, $7);
	$year += 2000 if $year < 50;
	$year += 1900 if 50 <= $year && $year <= 99;
	$mon = $month_names{$mon};
	my $mtime = timelocal($sec, $min, $hour, $mday, $mon, $year);
	return $mtime;
    } else {
	return -1; # return with error
    }
}

# calculate the time from string 
# INPUT is such as:  'Thu, 18 Dec 1997 21:09:43 GMT' (RFC 822 format)
# This routine allows a certain measure of fuzziness.
# timezone and its adjustment would be ignored such as GMT or +0900
sub get_date_fuzzily($) {
    my ($orig_str) = @_;
    my $str = $orig_str;
    my ($sec, $min, $hour, $mday, $mon, $year);
    my ($mtime);

    # remove a timezone adjustment such as '+0900'
    $str =~ s/(\+\d+)//;

    # get hour, min and sec.
    if ($str =~ s/\b($re_hour):($re_min):($re_sec)\b//) {
	$hour = $1;
	$min  = $2;
	$sec  = $3;
    } else {
	print STDERR "[$orig_str]:: lacks 'hour:min:sec'\n" if $DebugOpt;
	$hour = 0;
	$min  = 0;
	$sec  = 0;
    }

    # get a month
    if ($str =~ s/\b($re_month)\b//i) {
	$mon = $month_names{$1};
    } else {
	print STDERR "[$orig_str]:: lacks 'month'\n" if $DebugOpt;
	$mon = 0;
    }

    # get a day
    # this SHOULD be tried at the last save one.
    if ($str =~ s/\b($re_day)\b//i) {
        $mday = $1;
    } else {
        print STDERR "[$orig_str]:: lacks 'day'\n" if $DebugOpt;
        $mday = 0;
    }

    # get a year
    # this SHOULD be tried at the last.
    if ($str =~ s/\b($re_year)\b//i) {
        $year = $1;
    } else {
        print STDERR "[$orig_str]:: lacks 'year'\n" if $DebugOpt;
        $year = 1970;
    }

    # calculate
    $mtime = timelocal($sec, $min, $hour, $mday, $mon, $year);

    if ($DebugOpt) {
	print STDERR 
	    "DATE:: [$orig_str] -> $year, $mon, $mday, $hour, $min, $sec" .
		"->$mtime\n" if $DebugOpt;
    }
    return $mtime;
}

