#!/usr/bin/perl

# xml2iCal.pl
# Converts an Opie datebook.xml file into a iCal format calendar.ics
#
# damion@snapgear.com
#
# based on evolution-sync.pl by Roger Leardi roger@rogernet.com

# 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 1, 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.


# BACKUP YOUR FILES BEFORE RUNNING THIS.  THIS IS ALPHA CODE AT BEST. 

use Time::Local;

&load_zaurus_file();
&sort_em();
&write_evolution();

print "\n\nDONE!\n";


sub load_zaurus_file {

# This sub parses the xml file retrived from the zaurus 
# and loads the data into a hash keyed by SYNC ID	
	
	my $input;
	my $theresmore = 0;
	my $frontpeice = "";
	my $UID;
	
	if ( &check_file("./datebook.xml") == 0 ) { # Make sure we have read / write access
		my $datebook = "./datebook.xml";
		open(DATEBOOK_FILE, "< $datebook") or die "Could not open file !:$!";
		my $linecheck = 0;
		my $linebuf = "";
		while (defined($input = <DATEBOOK_FILE>)) {
			chomp $input;

			# This block of code makes sure that the input line is a full XML record if not it keeps reading lines
			# until it can build a full record before passing to the next block.
			
			if ( $input !~ m/.*>$/ ) { # This is not a full line
				if ( $linecheck == 1 ) {
					$linebuf = join " ", $linebuf, $input;
					$linecheck = 1;
					next;
				} else {
					$linebuf = $input;
					$linecheck = 1;
					next;
				}
			} else { 
				if ( $linecheck == 1 ) {
					$input = join " ", $linebuf, $input;
					$linebuf = "";
					$linecheck = 0;
				} else {
					$linebuf = "";
					$linecheck = 0;
				}
			};	
			
			next if ( $input =~ m/^<events>$/);
			if ( "$input" !~ m/^<event.*/i ) {
				push @datebook_header, $input;	# save anything thats not an appointment 
				next;				# for later		
			} else {
				@inputline = split " ", $input; # Split up the lines 
			
				while (defined($peice = ( shift @inputline))) {  # and sort out the peices
					next if ( $peice =~ m/^\\>$/);	
					next if ( $peice =~ m/^<event$/);	
					if ( $theresmore == 1 )	{ 
						if ( $front_peice ne "/>") {
							my $bigpeice = join " ", $front_peice, $peice;
							$peice = $bigpeice;
						}
					};
					if ( $peice !~ m/.*[^\\]"$/) { 
					 	$front_peice = $peice ;
						$theresmore = 1;
						next;
					} else {
						(my $key, my $data) = split /=/, $peice, 2 ;	
						$data =~ s/"(.*)"/$1/;
						$data =~ s/&quot;/"/g;
						$data =~ s/&amp;/&/g;
						$ZEVENT{$key} = $data;
						$theresmore = 0;
					};
				};
				
				# Now load all of the peices into a hash keyed by SYNC ID		
							
				if ( $ZEVENT{syncid} !~ m/^X-EVOLUTION-SYNC.*/ ) {
					$ZEVENT{syncid} = join ("-", 'X-EVOLUTION-SYNC', ( rand 10000 ), time() );
				}; 
				$UID = $ZEVENT{syncid};
	
				foreach my $key (keys(%ZEVENT)) { 
					$zaurus_vevent{$UID}{$key} = $ZEVENT{$key};
					$ZEVENT{$key} = "";
				};	
			};

		};

	} else {

		print "Error opening up the handhelds datebook.xml file ! Exiting ... \n";
		exit 1;

	};

};		


sub process_ev_alarm {    

# This sub wil process the alarms for evoluton.
	
	
	my $line = shift;
	my $key;
	my $data;
	my $time;
	
	if ( $line =~ m/END:VALARM/i ) {
		$data = join( ";-;", $VALARM{'TRIGGER'}, $VALARM{'ACTION'}, $VALARM{'UID'}, $VALARM{'DESCRIPTION'});
		push @{$VEVENT{ALARM}}, $data;
		%VALARM = ();
		return 0;
	} elsif ( $line =~ m/X-EVOLUTION-ALARM-UID.*/) {
		($key, $data) = split /:/, $line, 2;		
		$VALARM{'UID'}  = $data;
		return 1;
	} elsif ( $line =~ m/ACTION.*/ ){
		($key, $data) = split /:/, $line, 2;		
		$VALARM{'ACTION'}  = $data;
		return 1;
	} elsif ( $line =~ m/TRIGGER.*/) {
		($key, $data) = split /:/, $line, 2;		
		$VALARM{'TRIGGER'}  = $data;
		return 1;
	} elsif ( $line =~ m/DESCRIPTION.*/ ){
		($key, $data) = split /:/, $line, 2;		
		$VALARM{'DESC'}  = $data;
		return 1;
	};
	
	return 1;  	# If we get here we don't know what the line is but 
			# its still in the Valarm 
};	
	
sub process_vevent {

# This sub processes our VEVENTS.
# load every Vevent into a hash keyd by SYNC ID 

	my $UID; # Lets get our SYNC ID... or make one up if we don't have one
		
	if ( $VEVENT{'X-SID'} !~ m/^X-EVOLUTION-SYNC.*/ ) {
		$VEVENT{'X-SID'} = join ("-", 'X-EVOLUTION-SYNC', ( rand 10000 ), time()); 
	}; 
	$UID = $VEVENT{'X-SID'};


	foreach my $key (keys(%VEVENT)) { 
		if ( $key eq "ALARM" ) {
			@{$evolution_vevent{$UID}{$key}} = @{$VEVENT{$key}};
			@{$VEVENT{$key}} = (); 
		} else {	
			$evolution_vevent{$UID}{$key} = $VEVENT{$key};
		};
	};	
	%VEVENT = ();
};
	
sub process_vtodo {

# This sub processes our VTODO
# load every Vtodo into a hash keyd by SYNC ID 

	my $UID;
	if ( $VTODO{UID} == "" ) {
		$UID = join ("-", X-EVOLUTION-SYNC, ( rand 10000 ), time() );
	} else {
		$UID = $VTODO{UID};
	};
	
	foreach my $key (keys(%VTODO)) { 
		if ( $key eq "ALARM" ) {
			@{$evolution_vtodo{$UID}{$key}} = @{$VTODO{$key}};
			@{$VTODO{$key}} = (); 
		} else {	
			$evolution_vtodo{$UID}{$key} = $VTODO{$key};
		};
	};	
	%VTODO = ();
};


sub check_file {
	my $file = pop @_ ;	
	print "Checking: $file\n";
	if ( -e $file ) {
		if ( -T $file  && -w $file && -r $file ) {
			return 0;	
		};
	} else {
		return 1;
	};
};

sub erase_lists { 	# If we saw the sync id last time and now its gone
			# must mean we erased it.  -- unless your on the zaurus 
	my $key;	# if you modify a record there it erases the syncid and sets the uid to 0

	foreach $key (keys(%CONTROL)) {
		$ev_erlist{$key} = 1  unless $evolution_vevent{$key};
		$za_erlist{$key} = 1  unless $zaurus_vevent{$key};
	};
};	


sub sort_em {

	my $key;
	my @idlist;
	my $item;
	my %seen = ();
	my $winner;

	foreach $key (keys(%zaurus_vevent)) {
		push (@idlist, $key) unless $seen{$key}++;
	};
	

	# now process the list 
	
	foreach $item (@idlist) {
		&zaurus_master($item);
	};
};	


sub zaurus_master {

	
	my $key;
	my $rtype = "NA";
	my $rpos = "NA";
	my $rfreq = "NA";
	my $enddt = "NA";
	my $rweek = "NA";
	my $id = pop @_;
	unless ( $erased_list{$id} ) {
		foreach $key (keys (%{$zaurus_vevent{$id}})) {
#		  	print "Key is -->$key<-- data is -->$zaurus_vevent{$id}{$key}<--\n";
			if ( $key =~ m/description/ ) {
				$master_list{$id}{'DISCRP'} = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/location/i ) {
				$master_list{$id}{'LOC'} = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/created/ ) {
				$master_list{$id}{'TSTAMP'} = &zaurus_time_to_vcal($zaurus_vevent{$id}{$key});
			} elsif ( $key =~ m/syncid/ ) {
				$master_list{$id}{'SID'} = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/uid/ ) {
				$master_list{$id}{'UID'} = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/note/i ) {
				$master_list{$id}{'NOTE'} = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/categories/ ) {
				$master_list{$id}{'CATEGORIES'} = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/start/ ) {
				$master_list{$id}{'START'} = &zaurus_time_to_vcal($zaurus_vevent{$id}{$key});
			} elsif ( $key =~ m/^end$/ ) {
				$master_list{$id}{'END'} = &zaurus_time_to_vcal($zaurus_vevent{$id}{$key});
			} elsif ( $key =~ m/rtype/ ) {
					$rtype = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/rposition/ ) {
					$rpos = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/rfreq/ ) {
					$rfreq = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/enddt/ ) {
					$enddt = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/rweekdays/ ) {
					$rweek = $zaurus_vevent{$id}{$key};
			} elsif ( $key =~ m/alarm/ ) {
				my $time = $zaurus_vevent{$id}{$key};
				$time =~ s/(.*)/-PT$1M/;
				my $alarm = join( ";-;", $time, "DISPLAY", "ID1234", "DISC");
				push @{$master_list{$id}{'ALARM'}}, $alarm;  
			};	
		};
			
		if ( $rtype !~ m/NA|^$/ ) {
		my @rrule;
		my @wkdays;
		my $weekdays;
		my $thisday;		
		my $nopos = 0;
			if ( $rtype =~ m/Daily/ ) {
				$rtype = "FREQ=DAILY";
			} elsif ( $rtype =~ m/Weekly/ ) {
				$rtype = "FREQ=WEEKLY";
			} elsif ( $rtype =~ m/MonthlyDate/ ) {
				$thisday = ((localtime($zaurus_vevent{$id}{'start'})))[3];
				$thisday = "BYMONTHDAY=" . $thisday;
				$nopos = 1;
				$rtype = "FREQ=MONTHLY";
			} elsif ( $rtype =~ m/MonthlyDay/ ) {
				$thisday = (SU,MO,TU,WE,TH,FR,SA)[((localtime($zaurus_vevent{$id}{'start'})))[6]];
				$thisday = "BYDAY=" . $thisday;
				$rtype = "FREQ=MONTHLY";
			} elsif ( $rtype =~ m/Yearly/ ) {
				$rtype = "FREQ=YEARLY";
			};
			push @rrule, $rtype;
			
			if ( $rfreq !~ m/NA|^$/ ){
				$rfreq = join "=", "INTERVAL", $rfreq;
				push  @rrule, $rfreq;
			};
 
			if ( $rweek !~ m/NA|^$/ ) {
				if ( $rweek >= 64 ) {
					push @wkdays, "SU";
					$rweek -= 64;
				};
				if ( $rweek >= 32 ) {
					push @wkdays, "SA";
					$rweek -= 32;
				};
				if ( $rweek >= 16 ) {
					push @wkdays, "FR";
					$rweek -= 16;
				};
				if ( $rweek >= 8 ) {
					push @wkdays, "TH";
					$rweek -= 8;
				};
				if ( $rweek >= 4 ) {
					push @wkdays, "WE";
					$rweek -= 4;
				};
				if ( $rweek >= 2 ) {
					push @wkdays, "TU";
					$rweek -= 2;
				};
				if ( $rweek >= 1 ) {
					push @wkdays, "MO";
					$rweek -= 1;
				};
				
				my $daylist = join ",", @wkdays;
				$weekdays = join "=", "BYDAY", $daylist;
				push @rrule, $weekdays;
			};
		
			if ( $enddt !~ m/NA|^$/ ) {
			
				$enddt = &zaurus_time_to_vcal($enddt);
				$enddt =~ s/(\d*)T\d*/$1/;
				$enddt = "UNTIL=" . $enddt;
				push @rrule, $enddt;
			};	

			if ( $rpos !~ m/NA|^$/ ){
				$rpos = join "=", "BYSETPOS", $rpos;
				push  @rrule, $rpos unless ( $nopos == 1 );
			};

			push @rrule, $thisday if ($thisday);	
										
			my $list = join ";", @rrule;
				
			$master_list{$id}{'REPEAT'} = $list;

		};
	}; 
};


sub write_evolution {
	my $alarm=0;
	
	$evolution_input = "./calendar.ics";
	open(EVOLUTION_FILE, ">$evolution_input") or die "Could not open file !:$!";
		

	# Next lets put back all the stuff from the top of the file. 
	# But make sure not to put in the final </events> or </DATEBOOK>
	
	print EVOLUTION_FILE "BEGIN:VCALENDAR\n";
	foreach $key (keys (%master_list)) {
		my $seq = 1;
		
		$seq = $master_list{$key}{SEQUENCE} if ( $master_list{$key}{SEQUENCE} ); 

		my $bakline = join "::", $master_list{$key}{SID}, $seq;

	
		print EVOLUTION_FILE "BEGIN:VEVENT\n";
		print EVOLUTION_FILE "SUMMARY:$master_list{$key}{'DISCRP'}\n"   if ($master_list{$key}{'DISCRP'});
		print EVOLUTION_FILE "LOCATION:$master_list{$key}{'LOC'}\n"  if ($master_list{$key}{'LOC'});
#		print EVOLUTION_FILE "CATEGORIES:Business\n";	#$master_list{$key}{'CATEGORIES'}\"\n";
		print EVOLUTION_FILE "UID:$master_list{$key}{'UID'}\n"  if ($master_list{$key}{'UID'});
#		print EVOLUTION_FILE "X-SID:$master_list{$key}{'SID'}\n"  if ($master_list{$key}{'SID'});
			#  NEED TO ADD ALARM HERE !!!!!!
		while (defined( my $tmp1 = pop  ( @{$master_list{$key}{'ALARM'}} ))) {
			my $alm_trigger;
			my $alm_action;
			my $alm_uid;
			my $alm_desc;

			($alm_trigger, $alm_action, $alm_uid, $alm_desc ) = split /;-;/, $tmp1, 4;
			
			
			print EVOLUTION_FILE "BEGIN:VALARM\n";
			print EVOLUTION_FILE "TRIGGER;VALUE=DURATION;RELATED=START:$alm_trigger\n";
			print EVOLUTION_FILE "ACTION:$alm_action\n";
			print EVOLUTION_FILE "X-EVOLUTION-ALARM-UID:$alm_uid\n";
			print EVOLUTION_FILE "DESCRIPTION:$alm_desc\n";
			print EVOLUTION_FILE "END:VALARM\n";

		};
		print EVOLUTION_FILE "DTSTAMP:$master_list{$key}{'TSTAMP'}\n"  if ($master_list{$key}{'TSTAMP'});	
		print EVOLUTION_FILE "DTSTART:$master_list{$key}{'START'}\n"  if ($master_list{$key}{'START'});	
		print EVOLUTION_FILE "DTEND:$master_list{$key}{'END'}\n"  if ($master_list{$key}{'END'});	
		print EVOLUTION_FILE "TRANSP:$master_list{$key}{'TRANSP'}\n"  if ($master_list{$key}{'TRANSP'});	
		print EVOLUTION_FILE "SEQUENCE:$master_list{$key}{'SEQ'}\n"  if ($master_list{$key}{'SEQ'});	
		print EVOLUTION_FILE "CLASS:$master_list{$key}{'CLASS'}\n"  if ($master_list{$key}{'CLASS'});	
		print EVOLUTION_FILE "RRULE:$master_list{$key}{'REPEAT'}\n"  if ($master_list{$key}{'REPEAT'});	
		print EVOLUTION_FILE "DESCRIPTION:$master_list{$key}{'NOTE'}\n"  if ($master_list{$key}{'NOTE'});	
		print EVOLUTION_FILE "END:VEVENT\n";
	};	 
	print EVOLUTION_FILE "END:VCALENDAR\n";
	
	close EVOLUTION_FILE;
};


sub zaurus_time_to_vcal {
	
	my $year;
	my $month;
	my $day;
	my $hour;
	my $min;
	my $sec;
	my $wday;
	my $yday;
	my $isdst;	 
	my $zaurus_time = pop @_;
	my $time;
	
	$zaurus_time =~ s/"(\d*)"/$1/;

	($sec,$min,$hour,$day,$month,$year,$wday,$yday,$isdst) = localtime($zaurus_time);

	$sec =~ s/(\d)/0$1/ if ( $sec =~ m/^\d$/);
	$sec =~ "00" if ( $sec =~ m/^$/);
	$min =~ s/(\d)/0$1/ if ( $min =~ m/^\d$/);
	$min =~ "00" if ( $min =~ m/^$/);
	$hour =~ s/(\d)/0$1/ if ( $hour =~ m/^\d$/);
	$hour =~ "00" if ( $hour =~ m/^$/);
	$day =~ s/(\d)/0$1/ if ( $day =~ m/^\d$/);
	$day =~ "00" if ( $day =~ m/^$/);
	$month++;
	$month =~ s/(\d)/0$1/ if ( $month =~ m/^\d$/);
	$month =~ "01" if ( $month =~ m/^$/);

	$time =  join "", ( $year + 1900 ), $month, $day, "T", $hour, $min, $sec;
	return $time;
};

