#!/usr/bin/perl
# this ugly hack is by David Bremner, bremner@unb.ca
# it is hereby placed in the Public Domain, while I still can.
#
# This is really broken with respect to any changes other than
# to the timers. If you delete a task, it will not go away.
#
# This may corrupt your files, turn your family pet against you,
# and give you an unpleasant smell. Don't say I didn't warn you.
#
# $Id: karmma.pl 5570 2006-08-20 19:22:06Z bremner $
# Release 0.1
use Data::ICal;
use Data::Dumper;
use Date::ICal;
use Net::SFTP::Foreign;
use Net::SFTP::Foreign::Util qw( fx2txt );
use Net::SFTP::Foreign::Constants qw( :status );
use Config::Auto;
use File::Temp;
use File::Slurp qw(read_file write_file);
use Getopt::Std;
use strict;
our $opt_Z;
getopts('Z');
# add a new method without subclassing. Whee, try that in Java
package Data::ICal::Entry;
sub prop{
my $self=shift;
my $prop=shift;
my $ref=$self->property($prop);
if ($ref){
return $ref->[0]->value;
} else {
return undef;
}
}
package main;
sub round {
my($number) = shift;
return int($number + .5);
}
my $config=Config::Auto::parse();
print STDERR "Connecting to ",$config->{remotehost},"\n";
my $sftp = Net::SFTP::Foreign->new($config->{remotehost},debug=>0) || die("sftp: $!");
my $MF = new File::Temp;
$MF->unlink_on_destroy(0);
$MF->autoflush(1);
my $LC = new File::Temp;
my $localcopy=$LC->filename;
print STDERR "Fetching ",$config->{remotefile},"\n";
my $status=$sftp->get($config->{remotefile},$localcopy);
if (!defined($status)) {
die "fetch failed: ",$sftp->status;
}
my $remotedata=read_file($localcopy);
# this assuming Eunuchs line endings
$remotedata=~ s/\n\s*\n/\n/gs;
print STDERR "parsing remote data...";
my $remotecal=Data::ICal->new( data=>$remotedata) ||die "parsing $!";
print STDERR "done\n";
my $localfile=$config->{localfile} || die("no localfile specified");
my $remotefile=$config->{remotefile} || die("no remotefile specified");
my $caldata="";
open LF, "$localfile" || die "$!";
while(<LF>) {
next if m/^\s*$/;
$caldata .= $_;
}
print STDERR "parsing local data...";
my $localcal = Data::ICal->new( data=>$caldata) ||die "parsing $!";
print STDERR "done.\n";
my %evhash=();
my $evcount=0;
my $updatecount=0;
my @tasks=();
my %total=();
my %taskhash=();
my $zerohour=$opt_Z;
foreach my $entry (@{$localcal->entries}, @{$remotecal->entries}){
next if ($zerohour && $entry->ical_entry_type eq 'VEVENT');
if ( $entry->ical_entry_type eq 'VEVENT'){
$evcount++;
my $uid=$entry->prop('uid');
if (!defined($evhash{$uid})){
my $parent=$entry->prop('related-to');
$evhash{$uid}=$entry;
$total{$parent} += $entry->prop('x-kde-karm-duration');
}
} elsif ($entry->ical_entry_type eq 'VTODO'){
my $uid=$entry->prop('uid');
if (defined($taskhash{$uid})){
# note, that in the current version (3.4.2) of karm, the
# last modified time is the same for every task. So this is bogus-ish
my $t1=new Date::ICal($taskhash{$uid}->prop('last-modified'));
my $t2=new Date::ICal($entry->prop('last-modified'));
$taskhash{$uid}=$entry if ( $t1->compare($t2) <0);
$updatecount++;
} else {
$taskhash{$uid}=$entry;
}
} else {
die ("Unexpected iCal entry ". $entry->ical_entry_type);
}
}
printf STDERR "%d/%d unique events found\n",scalar(keys %evhash),$evcount;
print STDERR $updatecount." tasks updated\n";
print $MF '
BEGIN:VCALENDAR
PRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN
VERSION:2.0
';
foreach my $taskid (keys %taskhash){
my $task=$taskhash{$taskid};
my $tot=round($total{$taskid}/60);
my $str=$task->as_string;
# this is a grotesque hack
$str=~ s/^X-KDE-KARM-TOTALSESSIONTIME:.*$/X-KDE-karm-totalSessionTime: 0/m;
$str=~ s/^X-KDE-KARM-TOTALTASKTIME:.*$/X-KDE-karm-totalTaskTime: $tot/m;
print $MF $str;
}
foreach my $eventid (keys %evhash){
my $str=$evhash{$eventid}->as_string;
$str=~ s/^x-kde-karm-duration/X-KDE-karm-duration/im;
print $MF $str;
}
print $MF '
END:VCALENDAR
';
my $merged=read_file($MF->filename) || die "reading merged file";
print STDERR "backing up remote file\n";
my $status = $sftp->do_rename($remotefile,$remotefile. "." .time) ;
die fx2txt($status) if ($status != SSH2_FX_OK);
print STDERR "uploading merged file\n";
$sftp->put($MF->filename,$remotefile) || die "upload $!";
print STDERR "backing up local file\n";
rename($localfile, $localfile . ".bak") || die "local backup $!";
print STDERR "updating local file\n";
write_file($localfile,$merged) || die "local update $!";