#!/usr/bin/perl
#
# $Id: mh2Maildir,v 1.5 2004/12/15 08:36:39 cvsrslomkow Exp $
#
# Copyright Robin * Slomkowski, 2003
# homepage http://rslomkow.org/scripts/
# 
# This software is available under the GPL
# http://www.gnu.org/licenses/gpl.txt
#
# usage: mh2Maildir SourceDirectory/ [DestinationRoot/]
#
# This program converts a mh folder into a Maildir folder
# 
# The default is to use $HOME/Maildir as the destination directory,
#
# It does not recursivly do sub-directories.  My suggestion if that is what
# you want: find ./mhdir -type f -exec mh2Maildir {} \;
#
# It does NOT delete or modify in the original messages (other than atime)
# 
# It is all perl based and does not require any other programs to be installed.
#
# Know Bugs:
#
# 1) It does a poor job of guessing the date for the random sequence numbers
#    and file access/modification time. This doesn't matter for MOST mail
#    readers because they look in the header and parse the header better
#    than I do.
#
# 2) It doesn't respect the new flag for mh, everything it converts appears
#    to be already read. I just didn't bother to figure it out.
#


use strict ;
use Date::Parse;

my $BASE_DIR = "$ENV{'HOME'}/Maildir" ;
my $DEBUG = 0 ;

MAIN: {
    my($src_dir, $dir_name, $start_dir, $dst_dir) ;

    if ( $#ARGV < 0 ) {
    	usage() ;
    }

    $start_dir = shift @ARGV ;

    if ( "$ARGV[0]" ) {
        $dst_dir = shift @ARGV ;
    } else {
        $dst_dir = $BASE_DIR ;
    }

    if ( $dir_name = verify_src_dir($start_dir) ) {
        convert_dir($start_dir, "$dst_dir/.$dir_name" )  ;
    } else {
    	warn "ERROR: Could not verify $start_dir\n"
    }

exit 0 ; }

sub verify_src_dir {
# input a directory
# output the final part of the name.
    my ( $max, $name, $found_files ) ;
    $found_files = 0 ;
    $max = 10 ;
    $name = shift @_ ;
    opendir DIR, "$name" ;
    my $i = 0 ;
    foreach ( readdir DIR ) {
	print "DEBUG3: working on $_\n" if $DEBUG > 2 ;

        if ( $_ =~ /^,{0,1}\d+$/ ) {
                $found_files ++ ;
		last ;
        }
        $i ++ ;
	last if $i > $max ;
    }
    closedir DIR ;
    
    if ( $found_files > 0 ) {
	$name =~ s@/$@@ ;
        $name =~ s@^.*/@@ ;
        return $name ;
    } else {
	warn "DEBUG2: Didn't find any files\n" if $DEBUG > 1 ;
        return '' ;
    }
}

sub convert_dir {
    my $src_dir = shift @_ ;
    my $dst_dir = shift @_ ;
    my $msg_counter = 0 ;

    maildirmake ($dst_dir)  ;

    opendir DIR, "$src_dir" || die "$!: cannot open $src_dir\n" ;
    foreach ( readdir DIR ) {
	print "DEBUG1: print now working on $src_dir file $_\n" if $DEBUG > 1 ;
        if ( $_ =~ /^,{0,1}\d+$/ ) {
            my ($file,
	        $msg,
	        $time,
		$host,
		$messagecount,
		$flags,
		$messagefn,
		$conter) ;

	    $file = $_ ;
	    $flags = 'read' ;

	    if ( $file =~ /^,/ ) { 
	    	$flags .= 'deleted' ;
		$messagecount = $file ;
		$messagecount =~ s/^,// ;
		$host = 'mhdel' ;
	    } else {
	    	$messagecount = $file ;
		$host = 'mh' ;
	    }

	    # OK now lets read the file
            open DATA, "$src_dir/$file" ;

	    # we start inside the header
	    my $inheaders = 1 ;

            while (<DATA>) {

		# end headers
		if ( $_ eq "\n" ) {
		    $inheaders = 0 ;
		}

		# check for replied
		if ( $inheaders
		     && $_ =~ /^Replied: / 
		     && $flags !~ /reply/
		   ) {
		        $flags .= 'reply' ;
		}

		if ( $inheaders
		     && $_ =~ /(for .*; |id .*; |^\s+)(\w+, \d+ \w{3} \d{4} \d+:\d+:\d+ [+\-]\d{4})/
		   ) {
		       my $timestring = $2 ;
		       my $t = str2time($timestring) ;
		       print "DEBUG5: $timestring -> $t\n" if $DEBUG > 1 ;
		       if ( $t > $time ) {
		       	  $time = $t ;
		       }
		}

                $msg .= $_ ;
            }
            close DATA ;

	    if ( ! $time ) {
	    	$time = time() ;
		warn "WARN: no time found in $src_dir/$file" ;
	    }

            $messagefn = sprintf ("new/%d.%06d.$host:2,",
                                  $time,
                                  $messagecount) ;
	    #
	    # Append flag characters to the end of the
	    # filename, according to flag characters
	    # collected from the message headers
            
	    $messagefn .= 'F' if $flags =~ /F/; # Flagged.
	    $messagefn .= 'R' if $flags =~ /reply/; # Replied to.
	    $messagefn .= 'S' if $flags =~ /read/; # Seen or Read.
	    $messagefn .= 'T' if $flags =~ /deleted/; # Tagged for deletion.

	    ## my $filename = "$dst_dir/$messagefn" ;
	    print "DEBUG4: preparing to write: $dst_dir/$messagefn\n" if $DEBUG > 1 ;

	    # OK now write the file
##            sysopen (MAIL, $filename, 'O_RDWR|O_EXCL|O_CREAT', 0600) || die("$!: Fatal: unable to create new message $messagefn");
            open (MAIL, ">$dst_dir/$messagefn" ) || die("$!: Fatal: unable to create new message $messagefn");
            print MAIL $msg ;
            close MAIL ;
	    chmod 0600, "$dst_dir/$messagefn" ;
	    utime $time, $time, "$dst_dir/$messagefn" ;
	    $msg_counter ++ ;
	    print "DEBUG: wrote $dst_dir/$messagefn\n" if $DEBUG > 0 ;
        }
    }
    closedir DIR ;
    print "INFO: messages in ${dst_dir}: $msg_counter\n" ;
}

# The maildirmake function
# ------------------------
# 
# It does the same thing that the maildirmake binary that 
# comes with courier-imap distribution
#
sub maildirmake
{
    foreach(@_) {
        -d $_ or mkdir $_,0700 or die("Fatal: Directory $_ doesn't exist and can't be created.\n");
    
        -d "$_/tmp" or mkdir("$_/tmp",0700) or die("Fatal: Unable to make $_/tmp/ subdirectory.\n");
        -d "$_/new" or mkdir("$_/new",0700) or die("Fatal: Unable to make $_/new/ subdirectory.\n");
        -d "$_/cur" or mkdir("$_/cur",0700) or die("Fatal: Unable to make $_/cur/ subdirectory.\n");
    }
}

sub usage {
	print "usage: $0 SourceDirectory/ [DestinationRoot/]\n" ;
	exit 1 ;
}
