#!/usr/bin/perl -w use strict; use Data::Dumper; use Date::Parse; use Win32::OLE qw/EVENTS/; # (C) 2006 Randall Lucas. See license below. our $License = < outfile Reads the current user's Outlook calendar and translates appointments into 'Remind' format. Options: --help This message --get_body Retrives body of appointment entry in addition to the subject, but provokes an Outlook warning dialog box. --long_start_dates Verbosely adds a 'SATISFY' condition for the start date of recurring appointments that began in the past. Would be useful if traveling back in time. $License"; } for (@ARGV) { $Opt{get_body} = 1 if $_ =~ m/get_body/i; $Opt{eliminate_past_start_dates_for_recurrences} = 0 if $_ =~ m/long_start_dates/i; } my $O = Win32::OLE->GetActiveObject("Outlook.Application"); my $NS = $O->GetNameSpace('MAPI'); # the magic required incantation. my $C = $NS->GetDefaultFolder($olFolderCalendar); # numeric constant defined above my $Items = $C->Items; my (@nonrecurs, @recurs); for (my $i=1; $i<=$Items->Count; $i++) { my %out; my $I = $Items->Item($i); next unless defined $I; $out{'subject'} = $I->Subject; $out{'body'} = $I->Body if $Opt{get_body}; $out{'_start_dt'} = $I->Start->Date ." ".$I->Start->Time; $out{'start_time'} = str2time($out{'_start_dt'}); $out{'_end_dt'} = $I->End->Date ." ".$I->End->Time; $out{'end_time'} = str2time($out{'_end_dt'}); $out{'all_day_p'} = $I->AllDayEvent; $out{'recurring_p'} = $I->IsRecurring; my $datestring; if ($out{'recurring_p'}) { my $RP = $I->GetRecurrencePattern; $out{'recurrence_dow_mask'} = $RP->DayOfWeekMask; $out{'recurrence_interval'} = $RP->Interval; $out{'recurrence_instance'} = $RP->Instance; $out{'recurrence_type'} = $RP->RecurrenceType; $out{'_recurrence_start_dt'} = $RP->PatternStartDate->Date . " " . $RP->StartTime->Time; $out{'recurrence_start_time'} = str2time($out{'_recurrence_start_dt'}); $out{'_recurrence_end_date'} = $RP->PatternEndDate->Date; $out{'recurrence_end_time'} = str2time($out{'_recurrence_end_date'}); push @recurs, \%out; } else { push @nonrecurs, \%out; } } print "## Recurring items:\n" if @recurs; for (sort { $a->{start_time} <=> $b->{start_time} } @recurs) { print render_rem($_) . "\n"; } print "## Nonrecurring items:\n" if @nonrecurs; for (sort { $a->{start_time} <=> $b->{start_time} } @nonrecurs) { print render_rem($_) . "\n"; } sub render_rem { my $out = shift || return; my %out = %$out; my $rem; if ($out{recurring_p}) { my $dow = join ", ", dow_mask_to_day_list($out{'recurrence_dow_mask'}); my %rec_start; my %rec_end; my $rs = str_date($out{'recurrence_start_time'}); my $rt = str_timepart($out{'recurrence_start_time'}); my $re = str_date($out{'recurrence_end_time'}); my $ri = $out{'recurrence_interval'}; my $live = ($out{'recurrence_start_time'} > time) ? 1 : 0; my $at = ''; unless ($out{all_day_p}) { $at = "AT $rt"; } my $sat = ''; if($live && $Opt{eliminate_past_start_dates_for_recurrences}) { $sat = " SATISFY [trigdate() >= '$rs']"; } if ($out{'recurrence_type'} == 0) { # daily; $rem = "$rs *$ri $at UNTIL $re"; } elsif ($out{'recurrence_type'} == 1) { # weekly; # XXX known bug: repeating with interval other than 1 (each week) is not implemented warn "WARNING: I can't handle invervals != 1 for weekly recurrences" if $ri != 1; $rem = "$dow $at UNTIL $re$sat"; } elsif ($out{'recurrence_type'} == 2) { # monthly; # XXX known bug: repeating with interval other than 1 (each month) is not implemented warn "WARNING: I can't handle invervals != 1 for monthly recurrences" if $ri != 1; my $h = hashify_time($out{'recurrence_start_time'}); $rem = "$h->{mday} $at UNTIL $re$sat"; } elsif ($out{'recurrence_type'} == 3) { # month Nth; # XXX known bug: repeating with interval other than 1 (each month) is not implemented warn "WARNING: I can't handle invervals != 1 for monthly recurrences" if $ri != 1; warn "WARNING: multiple days of week for an Nth weekday of month type, won't work right" if $dow =~ m/,/; my $inst = ($out{'recurrence_instance'}||0 -1 )* 7 + 1; $rem = "$dow $inst $at UNTIL $re$sat" } elsif ($out{'recurrence_type'} == 5) { # yearly; my $inst = ($out{'recurrence_instance'} -1 )* 7 + 1; my $h = hashify_time($out{'recurrence_start_time'}); $rem = "$h->{mday} $h->{mon} $at UNTIL $re$sat" } elsif ($out{'recurrence_type'} == 6) { warn "UNIMPLEMENTED: I haven't bothered to figure out the use of this yet." } } else { # non-recurring: if ($out{all_day_p}) { my $d = str_date($out{'start_time'}); $rem = "$d"; } else { my $st = str_date($out{'start_time'}); my $t = str_timepart($out{'start_time'}); my $at = " AT $t"; my $dur = $out{'end_time'} ? " DURATION " . str_duration($out{'end_time'} - $out{'start_time'}) : ''; $rem = "$st$at$dur"; } } return "REM $rem MSG \%\"". msg_escape($out{'subject'}) . "\%\" " . msg_escape($out{'body'}); } #foreach (qw(34 1592435 242 34134)) { warn "str dur $_ " . str_duration($_); } sub msg_escape { my $in = shift; return '' unless defined $in; $in =~ s/\%/\%\%/gmi; $in =~ s/^ /\% /gmi; $in =~ s/\n/\%_/gmi; $in; } sub dow_mask_to_day_list { my $mask = shift; my @days; return () if $mask == 0; die "invalid mask" if $mask < 0; for (reverse sort { +$a <=> +$b } keys %DowMask) { next if $_ > $mask; return ($DowMask{$_}, dow_mask_to_day_list($mask - $_)); } } sub hashify_time { my $t = shift; my @l = localtime ($t); my %o = map { $Localtime{$_} => $l[$_] } keys %Localtime; $o{mon} = $Mon[$o{mon}]; $o{year} += 1900; $o{min} ||= '00'; $o{sec} ||= '00'; return \%o; } sub str_date { my $t = shift; my $h = hashify_time($t); my $out = "$h->{mday} $h->{mon} $h->{year}"; } sub str_timepart { my $t = shift; my $h = hashify_time($t); my $out = sprintf("%02d:%02d", $h->{hr},$h->{min}); } sub str_datetime { my $t = shift; my $h = hashify_time($t); my $out = str_date($t) . " " . str_timepart($t); } sub str_duration { my $t = shift; my $h = int($t/3600) || '00'; my $m = int(($t % 3600)/60) || '00'; return sprintf("%02d:%02d", $h,$m); } __END__ $Id: out2rem.pl,v 1.5 2006/06/27 20:38:27 lucas Exp $