#!/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 () { # 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 ; }