#!!!PERL!! -w

# $Header: /raid/cvsroot/rt/bin/rt-mailgate,v 1.3 2001/11/29 08:42:41 jesse Exp $
# (c) 1996-2001 Jesse Vincent <jesse@fsck.com>
# This software is redistributable under the terms of the GNU GPL


package RT;
use strict;
use vars qw($VERSION $Handle $Nobody $SystemUser);

$VERSION="!!RT_VERSION!!";


use lib "!!RT_LIB_PATH!!";
use lib "!!RT_ETC_PATH!!";

use RT::Interface::Email  qw(CleanEnv LoadConfig DBConnect
			     GetCurrentUser
			     GetMessageContent
			     CheckForLoops 
			     CheckForSuspiciousSender
			     CheckForAutoGenerated 
			     ParseMIMEEntityFromSTDIN
			     ParseTicketId 
			     MailError 
			     ParseCcAddressesFromHead
			     ParseSenderAddressFromHead 
			     ParseErrorsToAddressFromHead
			    );

#Clean out all the nasties from the environment
CleanEnv();

#Load etc/config.pm and drop privs
LoadConfig();

#Connect to the database and get RT::SystemUser and RT::Nobody loaded
DBConnect();

#Drop setgid permissions
RT::DropSetGIDPermissions();

use RT::Ticket;
use RT::Queue;
use MIME::Parser;
use File::Temp;
use Mail::Address;


#Set some sensible defaults 
my $Queue = 1;
my $time = time;
my $Action = "correspond";  

my ($Verbose, $ReturnTid, $Debug);
my ($From, $TicketId, $Subject,$SquelchReplies);
my ($status, $msg);

# {{{ parse commandline 

while (my $flag = shift @ARGV) {
    if (($flag eq '-v') or ($flag eq '--verbose')) {
	$Verbose = 1;
    }
    if (($flag eq '-t') or ($flag eq '--ticketid')) {
	$ReturnTid = 1;
    }
    
    if (($flag eq '-d') or ($flag eq '--debug')) {
	$RT::Logger->debug("Debug mode enabled\n");
	$Debug = 1;
      }
    
    if (($flag eq '-q') or ($flag eq '--queue')) {
	$Queue = shift @ARGV;
    } 
    if ($flag eq '--ticket-id-from-extension') {
       $TicketId = $ENV{'EXTENSION'};
    }
    if ($flag eq '--queue-from-extension') {
       $Queue = $ENV{'EXTENSION'};
    }
    if (($flag eq '-a') or ($flag eq '--action')) {
	  $Action = shift @ARGV;
    } 
    
    
}

# }}}

# get the current mime entity from stdin
my ($entity, $head) = ParseMIMEEntityFromSTDIN();

#Get someone to send runtime errors to;
my $ErrorsTo = ParseErrorsToAddressFromHead($head);

#Get us a current user object.
my $CurrentUser = GetCurrentUser($head, $entity, $ErrorsTo);

# We've already performed a warning and sent the mail off to somewhere safe ($RTOwner).
#  this is _exceedingly_ unlikely but we don't want to keep going if we don't have a current user

unless ($CurrentUser->Id) {
	exit(1);
}

my $MessageId = $head->get('Message-Id') || 
  "<no-message-id-".time.rand(2000)."\@.$RT::rtname>";

#Pull apart the subject line
$Subject = $head->get('Subject') || "[no subject]";
chomp $Subject;

# Get the ticket ID unless it's already set
$TicketId = ParseTicketId($Subject) unless ($TicketId);

#Set up a queue object
my $QueueObj = RT::Queue->new($CurrentUser);
$QueueObj->Load($Queue);
unless ($QueueObj->id ) {

  MailError(To => $RT::OwnerEmail,
                  Subject => "RT Bounce: $Subject",
                  Explanation => "RT couldn't find the queue: $Queue",
                  MIMEObj => $entity);

}

# {{{ Lets check for mail loops of various sorts.

my $IsAutoGenerated = CheckForAutoGenerated($head);

my $IsSuspiciousSender = CheckForSuspiciousSender($head);

my $IsALoop = CheckForLoops($head);


#If the message is autogenerated, we need to know, so we can not 
# send mail to the sender
if ($IsSuspiciousSender || $IsAutoGenerated || $IsALoop) {
    $SquelchReplies = 1;

    $ErrorsTo = $RT::OwnerEmail;
    
    #TODO: Is what we want to do here really 
    #  "Make the requestor cease to get mail from RT"?
    # This might wreak havoc with vacation-mailing users.
    # Maybe have a "disabled for bouncing" state that gets
    # turned off when we get a legit incoming message

}


# {{{ Warn someone  if it's a loop

# Warn someone if it's a loop, before we drop it on the ground
if ($IsALoop) {
    $RT::Logger->crit("RT Recieved mail ($MessageId) from itself.");
    
    #Should we mail it to RTOwner?
    if ($RT::LoopsToRTOwner) {
	MailError(To => $RT::OwnerEmail,
		  Subject => "RT Bounce: $Subject",
		  Explanation => "RT thinks this message may be a bounce",
		  MIMEObj => $entity);
	
	#Do we actually want to store it?
	exit unless ($RT::StoreLoops);
    }
}

# }}}


   #Don't let the user stuff the RT-Squelch-Replies-To header.
    if ($head->get('RT-Squelch-Replies-To')) {
        $head->add('RT-Relocated-Squelch-Replies-To',
                   $head->get('RT-Squelch-Replies-To'));
        $head->delete('RT-Squelch-Replies-To')
    }


if ($SquelchReplies) {
    ## TODO: This is a hack.  It should be some other way to
    ## indicate that the transaction should be "silent".

    my ($Sender, $junk) = ParseSenderAddressFromHead($head);
    $head->add('RT-Squelch-Replies-To', $Sender);
}

# }}}


# {{{ If we require that the sender be found in an external DB and they're not
# forward this message to RTOwner



if ($RT::LookupSenderInExternalDatabase && 
    $RT::SenderMustExistInExternalDatabase )  {

    MailError(To => $RT::OwnerEmail,
	      Subject => "RT Bounce: $Subject",
	      Explanation => "RT couldn't find requestor via its external database lookup",
	      MIMEObj => $entity);
    
}

# }}}


# {{{ elsif we don't have a ticket Id, we're creating a new ticket



elsif (!defined($TicketId)) {
    
    # {{{ Create a new ticket
    if ($Action =~ /correspond/) {
	
	#    open a new ticket 
	my @Requestors = ($CurrentUser->id);
	
	my @Cc;
	if ($RT::ParseNewMessageForTicketCcs) {
		@Cc = ParseCcAddressesFromHead(Head => $head, 
					CurrentUser => $CurrentUser,
					QueueObj => $QueueObj );
	}

	my $Ticket = new RT::Ticket($CurrentUser);
	my ($id, $Transaction, $ErrStr) = 
	  $Ticket->Create ( Queue => $Queue,
			    Subject => $Subject,
			    Requestor => \@Requestors,
			    Cc => \@Cc,
			    MIMEObj => $entity
			  );
	if ($id == 0 ) {
	    MailError( To => $ErrorsTo,
 		       Subject => "Ticket creation failed",
		       Explanation => $ErrStr,
		       MIMEObj => $entity
		     );
	    $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr ");
	}	
    }

    # }}}
    
    else {
	#TODO Return an error message
	MailError( To => $ErrorsTo,
		   Subject => "No ticket id specified",
		   Explanation => "$Action aliases require a TicketId to work on",
		   MIMEObj => $entity
		 );
	
	$RT::Logger->crit("$Action aliases require a TicketId to work on ".
			  "(from ".$CurrentUser->UserObj->EmailAddress.") ".
			  $MessageId);
    }
}

# }}}

# {{{ If we've got a ticket ID, update the ticket

else {
    
    #   If the action is comment, add a comment.
    if ($Action =~ /comment/i){
	
	my $Ticket = new RT::Ticket($CurrentUser);
	$Ticket->Load($TicketId);
	unless ($Ticket->Id) {
	    MailError( To => $ErrorsTo,
		       Subject => "Comment not recorded",
		       Explanation => "Could not find a ticket with id $TicketId",
		       MIMEObj => $entity
		     );
	    #Return an error message saying that Ticket "#foo" wasn't found.
	}
	
	($status, $msg) = $Ticket->Comment(MIMEObj=>$entity);
	unless ($status) {
	    #Warn the sender that we couldn't actually submit the comment.
	    MailError( To => $ErrorsTo,
		       Subject => "Comment not recorded",
		       Explanation => $msg,
		       MIMEObj => $entity
		     );
	}	
    }

    # If the message is correspondence, add it to the ticket
    elsif ($Action =~ /correspond/i) {
	my $Ticket = RT::Ticket->new($CurrentUser);
	$Ticket->Load($TicketId);
	
	#TODO: Check for error conditions
	($status, $msg) = $Ticket->Correspond(MIMEObj => $entity);
	unless ($status) {

	    #Return mail to the sender with an error
	    MailError( To => $ErrorsTo,
		       Subject => "Correspondence not recorded",
		       Explanation => $msg,
		       MIMEObj => $entity
		     );
	}
    }

    else {
	#Return mail to the sender with an error
	    MailError( To => $ErrorsTo,
		       Subject => "RT Configuration error",
		       Explanation => "'$Action' not a recognized action.".
		                      " Your RT administrator has misconfigured ".
		                      "the mail aliases which invoke RT" ,
		       MIMEObj => $entity
		     );

	$RT::Logger->crit("$Action type unknown for $MessageId");
	
    }
    
}

# }}}

$RT::Handle->Disconnect();


# Everything below this line is a helper sub. most of them will eventually
# move to Interface::Email

#When we call die, trap it and log->crit with the value of the die.
$SIG{__DIE__}  = sub {
    unless ($^S || !defined $^S ) {
        $RT::Logger->crit("$_[0]");
	MailError( To => $ErrorsTo,  
		   Bcc => $RT::OwnerEmail,
		   Subject => "RT Critical error. Message not recorded!",
		   Explanation => "$_[0]",
		   MIMEObj => $entity
		 );
	exit(-1);
    }
    else {
        #Get out of here if we're in an eval
        die $_[0];
    }
};



1;
