Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 193 lines (176 sloc) 6.82 KB
#!/usr/bin/perl
# vcalendar-filter is a simple filter to give plain text representations of vcards
# Copyright (C) 2008 Martyn Smith
#
# 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 3 of the License, 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# This script takes a simple VCALENDAR file as input on STDIN and produces a
# human readable text/plain representation of it on STDOUT
#
# It has been designed for use with mutt's auto_view config option, see the
# README file for more details
use strict;
use warnings;
use Data::ICal;
use Text::Autoformat;
use Date::Manip::DM6;
use Date::Manip::TZ;
my $body = eval { local $/ = undef; <> };
my $calendar = Data::ICal->new(data => $body);
# If parsing failed, try parsing as version 1.0
$calendar = Data::ICal->new(data => $body, vcal10 => 1) unless $calendar;
# If parsing failed, give up :-(
unless ( $calendar ) {
print "Unable to parse vcalendar: ", $calendar->error_message, "\n";
print $body;
exit 1;
}
my %values;
my $vevent = 0;
foreach my $entry ( @{$calendar->{entries}} ) {
my $properties;
foreach my $property ( keys %{$entry->properties} ) {
next unless defined $entry->property($property);
$properties->{$property} = join(', ', map { $_->decoded_value } @{$entry->property($property)});
if ( $property eq 'description' ) {
$properties->{$property} = autoformat $properties->{$property}, {
all => 1,
left => 15,
};
$properties->{$property} =~ s/^\s*// if defined $properties->{$property};
} elsif ( $property =~ m{ \A dt (?: start | end ) \z }xms ) {
if ( $properties->{$property} =~ m{ (\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d) }xms ) {
$properties->{$property} = "$1-$2-$3 $4:$5:$6";
}
}
}
if ( $entry->ical_entry_type eq 'VTIMEZONE' ) {
if ( defined $properties->{tzid} ) {
print "Timezone : ", $properties->{tzid}, "\n";
print "\n";
}
} elsif ( $entry->ical_entry_type eq 'VEVENT' ) {
if ($vevent++ > 0) {
print '-' x 72, "\n";
}
my $prevseen = 0;
foreach my $key ( qw(summary BR description BR location organizer dtstart dtend) ) {
if ( $key eq 'BR' && $prevseen > 0) {
print "\n";
next;
}
$prevseen = 0;
next unless defined $properties->{$key};
my $info = $properties->{$key};
$info =~ s/mailto://i;
$info =~ s/\s*$//;
next unless length($info) > 0;
$prevseen = 1;
my $timeentry = 0;
my $k = $key;
if ($key eq 'dtstart') {
$k = "starts";
$timeentry = 1;
} elsif ($key eq 'dtend') {
$k = "ends";
$timeentry = 1;
}
if ($timeentry > 0) {
# Check if 'TZID=<timezone>' was specified as a parameter.
my $tz;
foreach my $param ( @{$entry->property($key)} ) {
foreach my $parm (keys %{$param->parameters} ) {
if ($parm eq "TZID") {
$tz = ${$param->parameters}{$parm};
}
}
}
# Default to 'GMT' if not specified anywhere else
if (!defined($tz)) {
if (!defined($properties->{tzid})) {
$tz = "GMT";
} else {
$tz = $properties->{tzid};
}
}
# Convert to local timezone.
my $localinfo = Date_ConvTZ($info, $tz);
if (!defined($localinfo) || $localinfo eq '') {
# Some vcalendar TZIDs have a non standard format
# like '(UTC-06:00) Central Time (US & Canada)'.
# Attempt to extract a useful timezone and try again.
$tz =~ 'UTC([+-][0-2][0-9]:[0-6][0-9])';
$tz = $1;
$localinfo = Date_ConvTZ($info, $tz);
}
# Format result.
if (!defined($localinfo) || $localinfo eq '') {
# Still couldn't convert to local timezone, so
# report raw timestamp with original timezone.
$info = "$info $tz";
} else {
my $tzobj = new Date::Manip::TZ;
my $loctz = $tzobj->curr_zone();
$info = UnixDate($localinfo, "%Y-%m-%d %H:%M:%S $loctz");
}
}
printf "%-12s: %s\n", ucfirst $k, $info;
# Save processed value.
$values{$key} = $info;
}
} else {
print "WARNING: Unknown entry type: ", $entry->ical_entry_type, "\n";
}
if (exists($values{'dtstart'})) {
printf "\n%-12s:\n", 'Emacs diary';
my @dtstart = split(' ', $values{'dtstart'});
my $dtstart_time;
if (@dtstart > 1) {
$dtstart_time = $dtstart[1];
$dtstart_time =~ s/:00$//; # Strip silly seconds.
}
my $firstline = $dtstart[0];
if (defined($dtstart_time)) {
$firstline .= " " . $dtstart_time;
}
if (exists($values{'dtend'})) {
my @dtend = split(' ', $values{'dtend'});
my $dtend_time;
if (@dtend > 1) {
$dtend_time = $dtend[1];
$dtend_time =~ s/:00$//; # Strip silly seconds.
}
if ($dtstart[0] eq $dtend[0]) {
# Ends the same day it starts.
if (defined($dtend_time)) {
$firstline .= "-" . $dtend_time;
}
} else {
$firstline .= " - " . $dtend[0];
if (defined($dtend_time)) {
$firstline .= " " . $dtend_time;
}
}
}
if (exists($values{'organizer'})) {
$firstline .= " " . $values{'organizer'};
}
if (exists($values{'summary'})) {
$firstline .= " " . $values{'summary'};
}
printf "%s\n", $firstline;
if (exists($values{'location'})) {
printf " %s\n", $values{'location'};
}
}
}