The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
#
# read-imip.pl, November, 1999, Eric Busboom 
#
# Pass a MIME file with one or more iCal components to this program,
# and it will create a calendar from the name of the recipient,
# extract all of the components and stick them in the 'incoming'
# directory of the calendar.
#
# If you are using this with send-imp.pl, you probably want to use formail: 
#       send-imip.pl ../../test-data/rfc2446.ics | formail -ds ./read-imip.pl  
#
# The program will have no output if it accepts the component for
# storage in the calendar. It will output a reply message if the
# component has any significant errors
# 
# Note that each component in the cluster must be a VCALENDAR,
# hopefully with a VEVENT, VTODO or VJOURNAL embedded inside
#

use lib "../../blib/lib";
use lib "../../blib/arch";
use lib "../../lib";

use lib '/usr2/ebusboom/local/lib/perl';
use lib '/home/eric/proj/local/lib/perl5/site_perl/5.005/i386-linux/';


use Net::ICal;
use Net::ICal::Calendar;
use Net::ICal::Cluster;
use Getopt::Std;

use MIME::Entity;
use MIME::Parser;

sub add_part {
  my $output = shift;
  my $entity = shift;
  my $i;

  my $pre = $entity->preamble;
  my $head = $entity->head;
  my $id = $head->mime_attr('content-id');
  my $filename = $head->mime_attr('content-disposition.filename') || $id;
  
  push(@$output,[$entity->bodyhandle->as_string(),
		 $head->mime_attr('content-type'), 
		 $filename,
		$id]);
  
  if ($head->mime_attr('content-type') eq "multipart/alternative"){
    
    for($i=0; $i <$entity->parts; $i++){
      add_part($output,$entity->parts($i));
    }
  }
  
}

sub mime_parse {
  
  my @output;
  my $i;

  mkdir("mimemail",0775);
  my $parser = new MIME::Parser output_dir  => "./mimemail",
  output_prefix  => "part",
  output_to_core => 20000;
    
  $entity = $parser->read(\*STDIN) or die "couldn't parse MIME stream";
  
  my $header = $entity->head();
  my $body  = $entity->as_string() || "No Body";
  my $filename  = " ";

  if ($body){
    push(@output,[$body,$entity->effective_type, $filename," "]);
  }
  
  my $from = $header->get("From");
  chomp($from);
  my $to = $header->get('To');
  chomp($to);

  for($i=0; $i <$entity->parts; $i++){
    add_part(\@output,$entity->parts($i));
  }

  return ($from,$to,\@output);

}

sub return_error{
  my $to = shift;
  my $from = shift;
  my $orig = shift;
  my $text = shift;
  my $message = shift;
  my $comp = shift;

  $top = build MIME::Entity From    => $to,
  To      => $from,
  Subject => "iMIP message error",
  Data => $data,
  Type    =>"multipart/mixed";
  
  my $data = "In your iMIP message to $to, the following error occured:\n\n$message\n";

  if ($text ne undef){ 
    $data .= "Here is the text of the component as the parser understood it:\n\n$text\n\n";
  }

  $data .= "Here is the original text of the component:\n\n$orig\n\n";


  $top->attach (Data=>$data);


  $comp->convert_errors;
  $cal = MIME::Entity->build (Data        =>  $comp->as_ical_string(),
			      Type        => "text/calendar",
			      Encoding    => "7bit");
  
  $top->add_part($cal);
  
  $cal->head->replace('content-type', "text/calendar; method=$method; charset=US-ASCII");
  
  $top->print(\*STDOUT);
  print "\n\n";

  exit;
}

sub store_incoming{

  my ($from,$to,$parts) = mime_parse();


  die "No recipient" if !$to;
  
  mkdir ($to,0775);
  my $calendar = new Net::ICal::Calendar($to);
  
  #Net::ICal::icalcomponent_as_ical_string($calendar);
  
  die "No calendar" if !$calendar;
  
  my $incoming = $calendar->get_incoming();
  
  die "Can't get \'incoming\' cluster for calendar \'$to\'" if !$incoming;
  
  
  foreach $p (@$parts){
    
    my ($text,$type,$filename,$id) = @$p;
    my $comp;
    my $count = 0;
    
    if ($type eq "text/calendar"){
      $comp = new Net::ICal::Component(\$text);
      
      
      if (!$comp){
	return_error($to,$from,$text,undef,
		     "Found a text/component MIME part, but could not\n extract an iCal component from it\n");
	next;
      }


      if($comp->count_errors()>0){
	
	return_error($to,$from,$text,$comp->as_ical_string,
		     "Found a text/component MIME part, but it had one or more fatal \nparsing errors. (Currently all parsing errors are fatal.)\n\n",
		     $comp);
	next;
	
      }
      
      
      if ($comp->type() ne "VCALENDAR"){
	return_error($to,$from,$text,$comp->as_ical_string,
		     "The root component is not a VCALENDAR",
		     $comp);
	next;
      }
      
      
      # Add some special properties
      
      $comp->add(new Net::ICal::Property::X("X-LIC-SENDER",$from));
      
      
      # Check that the version and method properties are correct 
      
      my ($version_prop) = $comp->properties("VERSION");
      
      if (!$version_prop){
	return_error($to,$from,undef,$comp->as_ical_string,
		     "No version property",$comp);
	next;
      }
      
      my $version = $version_prop->get_value();
      
      if ($version < 2){
	return_error($to,$from,undef,$comp->as_ical_string,
		     "This version only handles iCal version 2.0",
		     $comp);
	next;
      }
      
      my ($method_prop) = $comp->properties("METHOD");
      
      if (!$method_prop){
	return_error($to,$from,undef,$comp->as_ical_string,
		     "No method property",
		     $comp);
	next; 
      }
      
      my $method = $method_prop->get_value();
      
      $incoming->add($comp);
      $incoming->commit();
    } else {
    }
  }
  
}


######################################################################
# Main routine
######################################################################

store_incoming();