#! /usr/bin/perl 
# 
# ***** BEGIN LICENSE BLOCK *****
# Zimbra Collaboration Suite CSharp Client
# Copyright (C) 2007, 2009, 2010, 2013, 2014, 2016 Synacor, Inc.
#
# 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,
# version 2 of the License.
#
# 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 <https://www.gnu.org/licenses/>.
# ***** END LICENSE BLOCK *****
# 
################################################################################
# Date           :  19/05/2007
# Name Of Script : ZmCleanIPlanetICS.pl 
# Description    :  Script to clean and standardize .ics files exported from iPlanet calender server
# Usage          :  perl ZmCleanIPlanetICS.pl -d <domain name> [-i <iPlanet file>] [-o <output file> ]
#                   [-l <log file>] [-h ]
#    
#                   -h: Display this help message
#                   -d: Domain to be used for attendees <domain name>
#                   -i: Input iPlanet file specified in <iPlanet file>
#                   -o: Output file specified in <output file>
#                   -l: File for logging <log file>
#                
#                   Example:
#                   perl ZmCleanIPlanetICS.pl -h
#                   perl ZmCleanIPlanetICS.pl -i input.ics -o output.ics -d domain.com -l cleanIcs.log
#                   perl ZmCleanIPlanetICS.pl -d testdomain.com
#
# Version        :  1.0
#
#******************************************************************************#
#
# Default files :
# Input  :  iPlanetIcs.ics
# Output :  cleanedIcs.ics
# Log    :  cleanIcs.log
#
# (In current directory)
#
#******************************************************************************#

use strict;

my $strt_cal_component = 0;
my $block = '';
my %Cal_Components = ();
my $uid_string;
my @uid = ();
my $f_org = 0;
my $f_att = 0;
my $attendee = '';
my @attlist = ();
my $flag = 0;
my $inValarm = 0;
my $inVtodo = 0;

my $Default_Domain = '';
my $Iplanet_File = '';
my $Zcs_File = '';
my $Log_File = '';
my $r;

# Number of characters before line folding
my $LINE_FOLD_LIMIT = 78;

# Default files for optional command line options

my $DEFAULT_IPLANET_FILE = 'iPlanetIcs.ics';
my $DEFAULT_ZCS_FILE = 'cleanedIcs.ics';
my $DEFAULT_LOG_FILE = 'cleanIcs.log';

# if OS is Windows do not use carriage return
if( $^O =~ /^MSWin/ ) {
	$r = '';
}
else {
	$r = "\r";
}

# Read Command line options
for( my $i = 0; $i <= $#ARGV; $i++ ) { 
	my $argv = $ARGV[$i];
	chomp $argv;
	if( ($argv eq "-d") && ($Default_Domain eq '') ) {
		$Default_Domain = lc( $ARGV[++$i] );
		if( $Default_Domain eq '' ) {
			::print_help();
			exit;
		}
		
	}
	elsif( ($argv eq "-i" ) && ($Iplanet_File eq '') ) {
		$Iplanet_File = $ARGV[++$i];
		if( $Iplanet_File eq '' ) {
			::print_help();
                        exit;
                }
	}
	elsif( ($argv eq "-o" ) && ( $Zcs_File eq '') ) {
                $Zcs_File = $ARGV[++$i];
		if( $Zcs_File eq '' ) {
                        ::print_help();
                        exit;
                }

        }
	elsif( ($argv eq "-l" ) && ( $Log_File eq '') ) {
                $Log_File = $ARGV[++$i];
		if( $Log_File eq '' ) {
                        ::print_help();
                        exit;
                }
        }
	else {
        	::print_help();		
		exit;
	}
}

# Check for domain name
if( $Default_Domain eq '') {
	print "\nDomain name not specified\n\n";
	print_help();
	exit;
}

# Set the log file to use
if( $Log_File eq '' ) {
	$Log_File = $DEFAULT_LOG_FILE;
}

# Open log file
unless( open( LOG, ">>$Log_File" ) ) {
	print STDERR "Cannot open log file $Log_File for writing : $!\n";
	exit;
}
	
# Log the startup information
print LOG "\n";
::write_log( 'cleanup_iPlanet_ics', 'START', "Process Started\n");

# Assign defaults if options are not given 
if( $Iplanet_File eq '' ) {
	$Iplanet_File = $DEFAULT_IPLANET_FILE;
	::write_log( 'cleanup_iPlanet_ics', 'INFO', 
	"Input file not found, taking default as: $DEFAULT_IPLANET_FILE\n");
}

if( $Zcs_File eq '' ) {
	$Zcs_File = $DEFAULT_ZCS_FILE;
	::write_log( 'cleanup_iPlanet_ics', 'INFO', 
	"Output file not found, taking default as: $DEFAULT_ZCS_FILE\n");
}

if( $Log_File eq $DEFAULT_LOG_FILE ) {
	::write_log( 'cleanup_iPlanet_ics', 'INFO', 
	"Log file not found, taking default as: $DEFAULT_LOG_FILE\n");
}

# Check for file name conflicts
if ( ( $Iplanet_File eq $Zcs_File ) || ( $Iplanet_File eq $Log_File ) || ( $Zcs_File eq $Log_File ) ) {
	::write_log( 'cleanup_iPlanet_ics', 'ERROR',
	"Error: Conflicting file names specified\n" );
	
	 ::error_exit();
}

unless( open( IPLANET, "<$Iplanet_File" ) ) {
	::write_log( 'cleanup_iPlanet_ics', 'ERROR',
	"Cannot open input file $Iplanet_File: $!\n");
	
	::error_exit();
}

unless( open( ZCS, ">$Zcs_File" ) ) {
	::write_log( 'cleanup_iPlanet_ics', 'ERROR',
        "Cannot open output file $Zcs_File for writing : $!\n");
	
	close( IPLANET );
	::error_exit();	
}

# Read input file
while( <IPLANET> ) {
	chomp;
	s/$r$//;
	if( ( /^BEGIN:VEVENT/ ) || ( /^BEGIN:VTODO/ ) || ( /^BEGIN:VJOURNAL/ ) ) {
		# A new block of calendar component is found
		# Initialize block specific variables
		$strt_cal_component = 1;
		$block = '';
		$f_org = 0;
        	$f_att = 0;
		$attendee = '';
		@attlist = ();
		if( /^BEGIN:VTODO/ ) {
                        $inVtodo = 1;
		}
	}
	if( 1 == $strt_cal_component ) {
		if( /^ / ) {
			if( 1 == $f_org ) {
				$f_att = 0;
				if(/ ;X-S1CS-EMAIL/) {	
					my @temp = split( /=/, $_ );
					 if($temp[1] !~ /@/)
                	                {$temp[1].='@' . $Default_Domain;}
        	                        my $organiser = "ORGANIZER:MAILTO:$temp[1]";
	                                ::fold($organiser);		
				}
			}
			elsif( 1 == $f_att ) {
				s/^ //;
				$attendee = $attendee.$_;
				$f_org = 0;
			}
			else {
				$block = $block.$_."$r\n";
			}
            		next;
		} else {
			# Add current attendee to attendee list
			push( @attlist, $attendee );
			$attendee = '';
            		$f_org = 0;
            		$f_att = 0;
        	}
		if( /^UID/ ) {
			@uid = ();
			$uid_string = $_;
			@uid = split( /UID:/, $uid_string );
			chomp $uid[1];
			$block = $block.$_."$r\n";
		}
		elsif( /^RECURRENCE-ID:/ ) {
			# Ignore this line to remove RECURRENCE-ID from the block
		}
		elsif( /^DTSTART;VALUE=DATE:/ ) {
			my @tmp = split( /:/, $_ );
			$block = $block.$_."$r\n";
			$block = $block."DTEND;VALUE=DATE:$tmp[1]$r\n";
		}
		elsif( /^ORGANIZER/ ) {
			$f_org = 1;
			my @temp = split( /=/, $_ );
			if($temp[0] !~ ";CN") {
				$temp[1] =~ s/\"//g;
				if($temp[1] !~ /@/)
				{$temp[1].='@' . $Default_Domain;}
				my $organiser = "ORGANIZER:MAILTO:$temp[1]";
				::fold($organiser);
			}
				
		}
		elsif( /^X-NSCP-ORGANIZER-EMAIL/ ){
            		# Ignore this line from the block		
        	}
		elsif( /^ATTENDEE;ROLE/ ) {
			$attendee = $_;
			$f_att = 1;
		}
		elsif( /^BEGIN:VALARM/ ) {
			$inValarm = 1;
			$block = $block.$_."$r\n";
		}
		elsif( /^END:VALARM/ ) {
			$inValarm = 0;
			$block = $block.$_."$r\n";
		}
		elsif( ( $inValarm == 1 ) && (/@/g) ) {
			if( /^ATTENDEE:MAILTO:/ ) {
				# OK
				 $block = $block.$_."$r\n";
			} else {
				my $tempAttende = 'ATTENDEE:MAILTO:'.$_;
				#$block = $block.$tempAttende."$r\n";
				::fold($tempAttende);
			}
		}
		elsif( ( /^END:VEVENT/ ) || ( /^END:VTODO/ ) || ( /^END:VJOURNAL/ ) ) {
			$strt_cal_component = 0;
			if( /^END:VTODO/ ) {
	                        $inVtodo = 0;
                	}

			if ( exists $Cal_Components{$uid[1]} ) {
				# Duplicate entry
			}
			else {
				# Unique entry found, enter in hash
				$Cal_Components{$uid[1]} = 1;
				
				# Standardize the ATTENDEE property, if present for each attendee
				foreach my $attend (@attlist) {
					if( $attend ne '' ) {
						my $prn_att = '';
						my $first = 0;
						my $att_found = 0;
						my @temp = split( /;/, $attend );
						my @tmp_att = split( /:/, $attend ); 

						foreach( @temp ) {
                        				# Remove X-NSCP* from attendee list
							if( /^X-NSCP/ ) { next; }
							if( /^X-S1CS-EMAIL/ ){
								$att_found = 1;
								my @t = split( /=/, $_ );
								my @tt = split(/:/, $t[1] );
								
								$prn_att = $prn_att.":MAILTO:$tt[0]";
							}
							if( $att_found == 0 ) {
								if( $first ) { $prn_att = $prn_att.";"}
								$prn_att = $prn_att.$_;
								$first = 1;
							}
						}
						if( $att_found == 0 ) {
							if($tmp_att[1] =~ /@/g) {
								$prn_att = $prn_att.":MAILTO:$tmp_att[1]";
							}
							else {
								$prn_att = $prn_att.":MAILTO:$tmp_att[1]\@$Default_Domain";
							}
						}
						::fold( $prn_att );
					}
				}
				
				$block = $block.$_."$r\n";
					
				# print the cleaned up block in output file
				print ZCS "$block";
			}
		}
		
		elsif( $inVtodo == 1 ) {
			if( /^EXDATE/ ) {
				# ignore this line
			}
			else {
				$block = $block.$_."$r\n";
			}
		}
		else {
			$block = $block.$_."$r\n";
		}
	} else {
		if($_ ne "") {
			print ZCS $_."$r\n";
		}
	}
}

# Closing all files
close( IPLANET );  
close( ZCS );

# Log end of process
::write_log( 'cleanup_iPlanet_ics', 'END', 
"Process completed successfully\n");

close( LOG );

#----------
# Write to Log file
#
sub write_log {
    my( $module, $type, $err_string ) = @_;

    # Get current time
    my( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime( time() );
    $year += 1900;
    $mon++;

    # Append 0 if in case of value is less then 10
    $sec = "0$sec" if( $sec < 10 );
    $min = "0$min" if( $min < 10 );
    $hour = "0$hour" if( $hour < 10 );
    $mday = "0$mday" if( $mday < 10 );
    $mon = "0$mon" if( $mon < 10 );

    # Append Time Stamp
    print LOG "$year-$mon-$mday $hour:$min:$sec [$$] ";
    print LOG "[$type] ";
    print LOG "[$module] ";
    print LOG $err_string;
}
#
# End Write to log
#----------

#----------
# Line fold after $LINE_FOLD_LIMIT characters
#
sub fold {
	my $str = shift;
	chomp $str;
	$str =~ s/$r$//;
	my @tmp_line = ();
	my $flag = 0;
	my @tmp = split( //, $str );
	$" = '';
	while( @tmp_line = splice( @tmp, 0, $LINE_FOLD_LIMIT ) ) {
		if ( $flag ) {
			 $block = $block." ";
		}
		$block = $block."@tmp_line$r\n";
		$flag = 1;
	}
	$" = " ";
}
#
# End line fold
#----------

#----------
# Print help
#
sub print_help {
    print "Usage: $0 -d <domain name> [-i <iPlanet file>]";
    print "[-o <output file> ] [-l <log file>] [-h help]\n";
    print "\t-h: display this help message\n";
    print "\t-d: use the domain specified in <domain name>\n";
    print "\t-i: use the input iPlanet file specified in <iPlanet file>\n";
    print "\t-o: use the output file specified in <output file>\n";
    print "\t-l: log to <log file>\n";
}
#
# End print help
#----------

#----------
# Error clean up routine
#
sub error_exit {
	::write_log( 'cleanup_iPlanet_ics', 'END',
        "Process terminated due to errors\n");

	close( LOG );
	exit;
}
#
# End error clean up routine
#----------

