#!/usr/bin/perl

use warnings;
use strict;
use File::Basename;
use File::stat;

sub usage {
    die <<EOF;
Usage: $0 pmmail-dir maildir

Convert a pmmail directory |pmmail-dir| to Maildir format and
place the results in |maildir|.  
EOF
}

usage() unless @ARGV == 2;

my ($pm_dir, $md_dir) = @ARGV;
-d $pm_dir or die "$pm_dir: $!\n";

# Create maildir structure
-d $md_dir or mkdir $md_dir or die "$md_dir: $!\n";
mkdir "$md_dir/$_" || die "could not create $md_dir/$_: $!\n"
  for 'cur', 'new', 'tmp';

my @files = glob("$pm_dir/*.msg");
my $time = time();
my $host = 'pmmail';
my $flags = 'S';
my $semantics = '2';

for my $in_fn (@files) {
    my ($delivery_id) = fileparse($in_fn, '.msg');
    my $out_fn = "$md_dir/cur/$time.$delivery_id.$host:$semantics,$flags";
    open my $out, '>', $out_fn
      or die "could not create $out_fn: $!\n";
    open my $in, '<', $in_fn
      or die "could not open $in_fn: $!\n";
    print "$in_fn -> $out_fn\n";
    while (<$in>) {
        s/\r\n/\n/;
        print $out $_;
    }
    close $out;

    my $mtime = stat($in_fn)->mtime;
    utime $mtime, $mtime, $out_fn;
}

__END__

=head1 NAME

pmmail-to-maildir -- Convert a PMMail directory to Maildir format

=head1 DESCRIPTION

$ pmmail-to-maildir pmmail-dir maildir

Convert a PMMail directory |pmmail-dir| to Maildir format and
place the results in |maildir|.  The resulting messages are
marked as read, and all other flags are unset.  Progress
is reported on stdout.

The standard Maildir structure (cur, tmp, new) is created in
C<maildir>.  The messages are placed in C<maildir/cur>; each
has the format C<time.id.host,2:flags>, where

=over

=item B<time> is the script start time in seconds

=item B<id> is the basename of the message (a unique id)

=item B<host> is 'pmmail'

=item B<flags> is always S

=back

We try to preserve the original message file timestamp;
failure is silent.

=head1 EXAMPLE

./pmmail-to-maildir.pl ~/pmmail/ursetto1.act/kralor1.fld ~/mfolder/kralor

=head1 RATIONALE

PMMail is an old OS/2 mail client I used in 1996.  All I have left of
it is a tarball containing the message data.

=head1 INTERNALS

=head2 Format

Messages are contained one per file (xxxxxx.msg) in folders named
'name.fld/'.  They're in plain UNIX format and can be used verbatim as
Maildir messages.  However, all lines end with CRLF; these need to be
converted to plain LF first.

There's also a file C<folder.bag> in every folder, which contains
dates and subjects and maps them to the message files; it may or
may not contain metadata.  This file is ignored.

=head2 Flags

The status I see in pmmail messages is not very sensical; perhaps
PMMail kept this status elsewhere.  I am converting messages over 10
years old and so I do not much care about flag fidelity; we will just
dump mail into cur/ and set the S (seen) flag, ignoring what's in
Status:.

I'm seeing 

  Status: O
  Status: RO
  Status: U          # huh?
  X-Status:          # usually in combination with Status: 

and anomalies:

  Status: 4.2.0
  X-Status: A        # in combo with Status: -- maybe an MS thing

DJB defines for maildir:

=over

=item Flag "P" (passed): the user has resent/forwarded/bounced this
message to someone else.

=item Flag "R" (replied): the user has replied to this message.

=item Flag "S" (seen): the user has viewed this message, though
perhaps he didn't read all the way through it.

=item Flag "T" (trashed): the user has moved this message to the
trash; the trash will be emptied by a later user action.

=item Flag "D" (draft): the user considers this message a draft;
toggled at user discretion.

=item Flag "F" (flagged): user-defined flag; toggled at user discretion.

=back

Map from Status: to maildir is:

  O -> Old -- place in cur/ (instead of new)
  F -> 'F'
  A -> 'R'
  R -> 'S'
  D -> 'T'

=head1 AUTHOR

Jim Ursetto (http://3e8.org)

=head1 LICENSE

Public domain

=cut
