#!/usr/bin/perl

# $Header: /mhub4/sources/imap-tools/MboxtoIMAP.pl,v 1.10 2012/03/20 16:47:00 rick Exp $

######################################################################
#  Program name   MboxtoIMAP.pl                                      #
#  Written by     Rick Sanders                                       #
#  Date           9 March 2008                                       #
#                                                                    #
#  Description                                                       #
#                                                                    #
#  MboxtoIMAP.pl is used to copy the contents of Unix                #
#  mailfiles to IMAP mailboxes.  It parses the mailfiles             #
#  into separate messages which are inserted into the                #
#  corresponging IMAP mailbox.                                       #
#                                                                    #
#  See the Usage() for available options.                            #
#                                                                    #
######################################################################

use Socket;
use FileHandle;
use Fcntl;
use Getopt::Std;
use IO::Socket;
use IMAP::Utils;

    init();
    @mailfiles = getMailfiles();

    connectToHost($imapHost, \$dst);
    login($imapUser,$imapPwd, $dst);
    namespace( $dst, \$dstPrefix, \$dstDelim );

    if ( $range ) {
      ($lower,$upper) = split(/-/, $range);
      Log("Migrating Mbox message numbers between $lower and $upper");
    }

    $msgs=$errors=0;
    foreach $mailfile ( @mailfiles ) {
       $owner = getOwner( "$mfdir/$mailfile" );
       if ( $mbxname and $mfile ) {
          $mbx = $mbxname;
       } else {
          @terms = split(/\//, $mailfile);
          $mbx = $terms[$#terms];
       }
       $mbx = mailboxName( $mbx,$dstPrefix,$dstDelim );

       $mbxs++;
       Log("Copying to mbx $mbx");

       if ( !isAscii( $mbx ) ) {
          # mbx name contains non-ASCII characters
          if ( $utf7 ) {
             $mbx = Unicode::IMAPUtf7::imap_utf7_encode( $mbx );
          } else {
             Log("The name $mbx contains non-ASCII characters.  To have it properly");
             Log("named in IMAP you must install the Unicode::IMAPUtf7 Perl module");
          }
       } 

       createMbx( $mbx, $dst ) unless mbxExists( $mbx, $dst );

       if ( $removeCopiedMsgs ) {
          unless( open(NEW, ">$mfdir/$mailfile.new") ) {
             Log("Can't open $mfdir/$mailfile.new: $!");
             exit;
          }
       }

       $msgnum=0;
       @msgs = readMbox( "$mfdir/$mailfile" );
       $msgcount = $#msgs+1;
       Log("There are $msgcount messages in $mailfile");
       foreach $msg ( @msgs ) {
          $msgnum++;
          Log("Copying message number $msgnum");
          @msgid = grep( /^Message-ID:/i, @$msg );
          ($label,$msgid) = split(/:/, $msgid[0]);
          chomp $msgid;
          trim( *msgid );

          if ( $getdate ) {
             $date = get_date( $msg );
          }
             
          $date = get_date( $msg );

          my $message;
          foreach $_ ( @$msg ) { 
             chomp;
             $message .= "$_\r\n"; 
          }
 
          if ( $range ) {
             if ( ($msgnum < $lower) or ($msgnum > $upper) ) {
                #  We aren't going to copy this msg so save it to
                #  the temp copy of the mailfile that we are building
                print NEW "$message\n" unless $removeCopiedMessages;
                next;
             }
          }

          if ( insertMsg($mbx, \$message, $flags, $date, $dst) ) {
             $added++;
             print STDOUT "   Added $msgid\n" if $debug;
             print NEW "$message\n" unless $removeCopiedMsgs;
          }
       }
    
       if ( $removeCopiedMsgs ) {
          #  Put the temp mailfile less the copied messages in place.
          close NEW;
          $stat = rename( "$mfdir/$mailfile.new", "$mfdir/$mailfile" );
          unless ( $stat ) {
             Log("Rename $mfdir/$mailfile.new to $mfdir/$mailfile failed: $stat");
          } else {
             chown($owner, -1, "$mfdir/$mailfile");
             Log("Installed new version of mailfile $mfdir/$mailfile");
          }
       }
    }

    logout( $dst );

    Log("\n\nSummary:\n");
    Log("   Mailboxes  $mbxs");
    Log("   Total Msgs $added");


    exit;


sub init {

   if ( !getopts('m:L:i:dIr:RDf:n:p:') ) {
      usage();
      exit;
   }

   $mfdir    = $opt_m;
   $mfile    = $opt_f;
   $mbxname  = $opt_n;
   $logfile  = $opt_L;
   $range    = $opt_r;
   $root_mbx = $opt_p;
   $showIMAP = 1 if $opt_I;
   $debug    = 1 if $opt_d;
   $getdate = 1 if $opt_D;
   $removeCopiedMsgs = 1 if $opt_R;

   ($imapHost,$imapUser,$imapPwd) = split(/\//, $opt_i);
   IMAP::Utils::init();
   if ( $logfile ) {
	openLog($logfile);
   }
   Log("Starting");

   #  Determine if the IMAP Utf7 module is installed.

   eval 'use Unicode::IMAPUtf7';
   if ( $@ ) {
      # Module not installed
      $utf7 = 0;   
   } else {
      $utf7 = 1;
   }

}


sub getMailfiles {

my @mailfiles;

   #  Get a list of the mailfiles to be processed.  The
   #  user can either supply a directory name where one or
   #  more mailfiles reside or he can give a complete filepath
   #  and name of a single mailfile.

   if ( $mfdir ) {
      opendir D, $mfdir;
      @filelist = readdir( D );
      closedir D;

      foreach $fn ( @filelist ) {
         next if $fn =~ /\.|\.\./;
         push( @mailfiles, $fn );
      }
   } elsif ( $mfile ) {
      if ( !-e $mfile ) {
         Log("$mfile does not exist.");
         print STDOUT "mfile $mfile does not exist\n";
         exit;
      }
      push( @mailfiles, $mfile );
   }

   Log("No mailfiles were found in $dir") if $#mailfiles == -1;

   @mailfiles = sort { lc($a) cmp lc($b) } @mailfiles;

   return @mailfiles;
}



sub usage {

   print "Usage: iu-mboxtoimap\n";
   print "    -m <location of mailfiles>\n";
   print "    -f <file spec of individual mailfile>\n";
   print "    -n <mailbox name> Used with -f <mailfile>\n";
   print "    -i <server/username/password>\n";
   print "    [-r <range of messages>]  eg 1-10 or 450-475\n";
   print "    [-R remove copied messages from the mailfile]\n";
   print "    [-p <root mbx> put all mailboxes under the root mbx\n";
   print "    [-L <logfile>]\n";
   print "    [-d debug]\n";
   print "    [-I log IMAP protocol exchanges]\n";

}

sub readMbox {

my $file  = shift;
my @mail  = ();
my $mail  = [];
my $blank = 1;
local *FH;
local $_;

    open(FH,"< $file") or die "Can't open $file";

    while(<FH>) {
        s/
$//;
        if($blank && /\AFrom .*\d{4}/) {
            push(@mail, $mail) if scalar(@{$mail});
            $mail = [ $_ ];
            $blank = 0;
        }
        else {
            $blank = m#\A\Z#o ? 1 : 0;
            push(@{$mail}, $_);
        }
    }

    push(@mail, $mail) if scalar(@{$mail});
    close(FH);

    return wantarray ? @mail : \@mail;
}

sub insertMsg {

my $mbx = shift;
my $message = shift;
my $flags = shift;
my $date  = shift;
my $conn  = shift;
my ($lsn,$lenx);

   Log("   Inserting message") if $debug;
   $lenx = length($$message);

   if ( $debug ) {
      Log("$$message");
   }

   ++$lsn;
   $flags =~ s/\\Recent//i;

   sendCommand ($conn, "$lsn APPEND \"$mbx\" () \"$date\" \{$lenx\}");
   readResponse ($conn);
   if ( $response !~ /^\+/ ) {
       # next;
       push(@errors,"Error appending message to $mbx for $user");
       return 0;
   }

   print $conn "$$message\r\n";

   undef @response;
   while ( 1 ) {
       readResponse ($conn);
       if ( $response =~ /^$lsn OK/i ) {
	   last;
       }
       elsif ( $response !~ /^\*/ ) {
	   Log ("unexpected APPEND response: $response");
	   # next;
	   return 0;
       }
   }

   return 1;
}

sub mailboxName {

my $mbx       = shift;
my $dstPrefix = shift;
my $dstDelim  = shift;
my $dstmbx;

   #  Insert the IMAP server's prefix (if defined) and replace the Unix
   #  file delimiter with the server's delimiter (again if defined).

   $dstmbx = "$dstPrefix$mbx";
   $dstmbx =~ s#/#$dstDelim#g;

   if ( $root_mbx ) {
      #  Put folders under a 'root' folder on the dst
      $dstmbx =~ s/^$dstPrefix//;
      $dstDelim =~ s/\./\\./g;
      $dstmbx =~ s/^$dstDelim//;
      $dstmbx = $dstPrefix . $root_mbx . $dstDelim . $dstmbx;
      if ( uc($srcmbx) eq 'INBOX' ) {
         #  Special case for the INBOX
         $dstmbx =~ s/INBOX$//i;
         $dstmbx =~ s/$dstDelim$//;
      }
      $dstmbx =~ s/\\//g;
   }

   return $dstmbx;
}

sub getOwner {

my $fn = shift;
my $owner;

   #  Get the numeric UID of the file's owner
   @info = stat( $fn );
   $owner = $info[4];

   return $owner;
}

sub get_date {

my $msg = shift;

   #  Extract the date from the message and format it
          
   my @date = grep( /^Date:/i, @$msg );
   my ($label,$date) = split(/:/, $date[0],2);
   $date =~ s/^\s+|\s+$//g;
   $date =~ s/\s+/ /g;

   if ( $date =~ /,/ ) {
      ($dow,$date) = split(/,\s*/, $date);
   } 
   if ( $date =~ /\((.+)\)/ ) {
      $date =~ s/\($1\)//g;
   }
   $date =~ s/ /-/;
   $date =~ s/ /-/;
   chomp $date;
   $date =~ s/^\s+|\s+$//g;

   return $date;
}
