#!/usr/bin/perl -w

# tobit-mbox.pl - Convert Tobit mail folder to mbox format and/or move to Cyrus spool
# Copyright (C) 2008 by Dominik George
#
# 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 version 3 of the License, 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, see <http://www.gnu.org/licenses/>. 

# Helper function to escape strings for regex compatibility
sub regex_escape {
 my $string = $_[0];
 
 $string =~ s/\\/\\\\/g;
 $string =~ s/\//\\\//g;
 $string =~ s/\./\\./g;
 $string =~ s/\+/\\+/g;
 $string =~ s/\?/\\?/g;
 $string =~ s/\^/\\^/g;
 $string =~ s/\$/\\\$/g;
 $string =~ s/\|/\\|/g;
 $string =~ s/\(/\\(/g;
 $string =~ s/\)/\\)/g;
 $string =~ s/\[/\\]/g;
 $string =~ s/\]/\\]/g;
 $string =~ s/\{/\\{/g;
 $string =~ s/\}/\\}/g;

 return $string;
}
                

# Print start message
print ">>> Tobit mail folder to mbox/Cyrus converter\n";
print ">>> Copyright (c) 2008 by Dominik George\n\n";

print ">>> This program comes with ABSOLUTELY NO WARRANTY It is free software,\n";
print ">>> and you are welcome to redistribute it under certain conditions.\n";
print ">>> See source code for details!\n\n";

# Define and initialize variables
my $mbox     = 1;
my $count    = 0;
my $i        = 1;
my $tobitdir = ".";
my $mboxfile = "./tobit.mbx";

# For Cyrus support
my $cyrus     = 0;
my $imap;
my $cyrserver = "localhost";
my $cyruser   = "cyrus";
my $cyrgrp    = "mail";
my $cyrbin    = "/usr/lib/cyrus/bin";
my $cyrbox    = "";
my $cyrdir    = ".";
my $cyrnumber = 0;

# Parse command-line arguments
for (@ARGV) {
 if ($_ =~ /^--help/) {
  print "Usage: $0 [OPTIONS]\n\n";
  
  print "MBOX conversion (default)):\n";
  print "\t--no-mbox\t\tDisable mbox conversion\n";
  print "\t--dir=DIRECTORY\t\tLook for tobit files in DIRECTORY\n";
  print "\t--mbox=FILENAME\t\tWrite mbox to FILE\n";
  print "\t--help\t\t\tPrint this help screen\n\n";
  
  print "CYRUS conversion (activated by --cyrus parameter)\n";
  print "\t--cyrus\t\t\tActivate writing to Cyrus mail server\n";
  print "\t--cyruser=USER\t\tUser who can administer Cyrus\n";
  print "\t--cyrgrp=GROUP\t\tGroup mail spool files belong to\n";
  print "\t--cyrbin=DIR\t\tPath to Cyrus binaries\n";
  print "\t--cyrdir=MAILDIR\tFilesystem path to mailbox to use\n";
  print "\t--cyrbox=MAILBOX\tInternal mailbox name for the maildir\n";
  
  exit 0;
 } elsif ($_ =~ /^--dir=(.*)\/?$/) {
  $tobitdir = $1;
 } elsif ($_ =~ /^--mbox=(.*)/) {
  $mboxfile = $1;
 } elsif ($_ =~ /^--no-mbox$/) {
  $mbox = 0;
 } elsif ($_ =~ /^--cyrus$/) {
  $cyrus = 1;
 } elsif ($_ =~ /^--cyruser=(.*)/) {
  $cyruser = $1;
 } elsif ($_ =~ /^--cyrbin=(.*)\/?$/) {
  $cyrbin = $1;
 } elsif ($_ =~ /^--cyrdir=(.*)\/?$/) {
  $cyrdir = $1;
 } elsif ($_ =~ /^--cyrbox=(.*)/) {
  $cyrbox = $1;
 }
}

# Give the user some information
print "Looking for Tobit mail folder in $tobitdir .\n";

if ($mbox == 1) {
 print "Writing mbox output to $mboxfile .\n";
}

if ($cyrus == 1) {
 print "Writing to Cyrus mail folder $cyrdir .\n";
}

if (($cyrus == 0) && ($mbox == 0)) {
 die "No run mode specified (neither mbox nor Cyrus)!\n";
}

# Find all .0tx files in tobit folder
@files = glob($tobitdir . "/*.0tx");
$count = $#files + 1;
print "Found $count mail files (*.0tx).\n\n";

# Only operate if we have at least one mail file
if ($count == 0) {
 die "No mail files found!\n";
}

# Open whatever we are operating on
if ($mbox == 1) {
 # Open mbox file for writing
 open(MBOXFILE, ">" . $mboxfile) || die "Could not open mbox file $mbox!\n";
}

if ($cyrus == 1) {
 # Security question
 print "WARNING: This script can do serious harm to your Cyrus mailbox.\n";
 print "If you want to continue ON YOUR OWN RISK, type upper case \"yes\" now: ";
 my $confirm = <STDIN>;
 
 if ($confirm eq "YES\n") {
  print "\n";
 } else {
  die "User chose not to continue.\n";
 }

 #Find highest mail number in defined mailbox
 my @mailfiles = glob($cyrdir . "/*.");
 my $number = $mailfiles[$#mailfiles];
 $number =~ /\/(\d*).$/;
 $cyrnumber = $1 + 1;
}

print "Converting mail files now ...\n";

# Iterate through tobit files
for (@files) {
 # Read entire tobit file into array
 open(TOBITFILE, "<" . $_) || die "Unable to open $_!\n";;
 my @lines = <TOBITFILE>;
 close(TOBITFILE);

 # Define some local variables
 my $filterdone = 0;
 my $filterkey  = "";
 my $content    = "";
 my $from       = "";
 my $date       = "";
 my $line       = "";

 # Work on every single line from the tobit file
 for (@lines) {
  my $line = $_;

  # Filter binary characters and double headers
  if ($filterdone == 0) {
   if ($filterkey eq "") {
    # Find first line containing a mail header
    if ($line =~ /([\w-]*:\s.*)/) {
     $filterkey = regex_escape($1);
    }

    # Discard line
    $line = "";
   } else {
    # Find next line containing same header as first
    if ($line =~ /$filterkey/) {
     # Done filtering
     $filterdone = 1;
    } else {
     # Discard line
     $line = "";
    }
   }
  }
   
  # Append line to output
  $content .= $line;
  
  # Extract information for mbox header
  if ($line =~ /^From:.*<(.*)>/) {
   $from = $1;
  } elsif ($line =~ /^Date:\s*(\w{3}),\s*(\s\d{1,2})\s*(\w{3})\s*(\d{4})\s*(\d{1,2}:\d{2}:\d{2})/) {
   $date = $1 . " " . $3 . " " . $2 . " " . $5 . " " . $4;
  }
 }
 
 # Convert complete output to UNIX linebreaks, just to make sure ...
 $content =~ s/\r\n/\n/g;

 if ($mbox == 1) { 
  # Write mail to mbox file
  print MBOXFILE "From " . $from . " " . $date . "\n" . $content . "\n";
 }

 if ($cyrus == 1) {
  # Write to IMAP folder
  open(CYRFILE, ">" . $cyrdir . "/" . $cyrnumber++ . ".");
  print CYRFILE $content;
  close(CYRFILE);
 }

 # Show progress
 print "Converted $i of $count messages ...\r";;
 $i++;
}

print "\n\n";

# Close and write mbox file
close(MBOXFILE);

if ($cyrus == 1) {
 # Reconstruct mailbox
 print "Reconstructing cyrus mailbox $cyrbox as user $cyruser .\n";
 system("chown -R $cyruser:$cyrgrp $cyrdir");
 system("su $cyruser -c '$cyrbin/reconstruct -r $cyrbox'");
}

# Defined return
exit 0

