[Thread Prev][Thread Next][Thread Index]
[PilotMgr] Change to SynCM
- To: pilotmgr@xxxxxxxxxxxxxxxxx
- Subject: [PilotMgr] Change to SynCM
- From: Gunnar Stahl - Sun Sweden - Business Analyst eSun IT <Gunnar.Stahl@xxxxxxxxxxxxxxxxxxxx>
- Date: Fri, 17 Mar 2000 11:25:17 +0100 (MET)
- Reply-to: Gunnar Stahl - Sun Sweden - Business Analyst eSun IT <Gunnar.Stahl@xxxxxxxxxxxxxxxxxxxx>
- Sender: owner-pilotmgr@xxxxxxxxxxxxxxxxxxxx
Some time ago I think I saw a request for a fix to a problem (minor though) I
also have.
Haven't seen a solution broadcasted yet so here is my very quick fix.
The problem is:
Users entering ONLY a start-time in calendar manager don't see those
appointment moved to the Pilot. SynCM complains about "unsupported" date
(Start time after End time).
My solution is to set end-time = start-time and then have it imported to the
Pilot. I have kept error-messages though just to point out the fact. The
appointment is NOT changed in cm, so the error message appears for every sync.
Modifying the code to not display the error message is trivial.
Attached is the modified cm.pm and a file with diffs to the original.
Regards
/Gunnar Ståhl
Sun Microsystems Sweden.
# Copyright (c) 1997 Sun Microsystems, Inc.
# All rights reserved.
#
# Permission is hereby granted, without written agreement and without
# license or royalty fees, to use, copy, modify, and distribute this
# software and its documentation for any purpose, provided that the
# above copyright notice and the following two paragraphs appear in
# all copies of this software.
#
# IN NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE TO ANY PARTY FOR
# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
# OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF SUN
# MICROSYSTEMS, INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# SUN MICROSYSTEMS, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THE SOFTWARE PROVIDED
# HEREUNDER IS ON AN "AS IS" BASIS, AND SUN MICROSYSTEMS, INC. HAS NO
# OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
# MODIFICATIONS.
package SyncCM::cm;
use Calendar::CSA;
use Carp;
use strict;
use Time::Local;
use SyncCM::timeops;
use POSIX;
use sigtrap;
my (%NeutralRepeat, %CmRepeat, %PrivacyTable);
my ($DEFAULTS);
%NeutralRepeat =
(
'D(\d+)\s*#(\d+)\s*(\d+T\d+Z)?' => ["daily", "<x>"],
'MD(\d+)\s*#(\d+)\s*(\d+T\d+Z)?' => ["monthly-date", "<x>"],
'MP(\d+)\s*([SU MO TU WE TH FR SA \d-]+?)\s*#(\d+)\s*(\d+T\d+Z)?'
=> ["monthly-day", "<x>"],
'YM(\d+)\s*#(\d+)\s*(\d+T\d+Z)?' => ["yearly", "<x>"],
# Special case here. $2 is flags instead of count
# Count is ignored, anyway.
#
'W(\d+)\s*([SU MO TU WE TH FR SA]+?)\s*#\d+\s*(\d+T\d+Z)?'
=> ["weekly", "<x>", 0],
'.*' => ["not-supported", "<x>", 0],
);
%CmRepeat =
(
"daily" => 'D<x> #<y>',
"weekly" => 'W<x> <d> #<y>',
"monthly-date" => 'MD<x> #<y>',
"monthly-day" => 'MP<x> <f> #<y>',
"yearly" => 'YM<x> #<y>',
);
%PrivacyTable =
(
"Show Time and Text" => Calendar::CSA::CSA_CLASS_PUBLIC,
"Show Time only" => Calendar::CSA::CSA_CLASS_CONFIDENTIAL,
"Show Nothing" => Calendar::CSA::CSA_CLASS_PRIVATE,
Calendar::CSA::CSA_CLASS_PUBLIC => "Show Time and Text",
Calendar::CSA::CSA_CLASS_CONFIDENTIAL => "Show Time only",
Calendar::CSA::CSA_CLASS_PRIVATE => "Show Nothing",
);
sub setup
{
($DEFAULTS) = @_;
}
sub deleteCalendar
{
my ($session) = @_;
$session->delete_calendar;
}
sub createCalendar
{
my ($cal) = @_;
my ($user) = $cal;
$user = $1 if ($user =~ /([^@]*)@/);
Calendar::CSA::add_calendar(
{
"user_name" => $user,
"user_type" => 'INDIVIDUAL',
"calendar_address" => $cal,
},
'Access List' =>
{
'type' => 'ACCESS LIST',
'value' =>
[
{
'user' =>
{
"user_name" => $ENV{'USER'},
"calendar_address" => $cal,
"user_type" => 'INDIVIDUAL',
},
'rights' => ['OWNER RIGHTS'],
},
],
},
'Character Set' =>
{
'type' => 'STRING',
'value' => 'en_US.ISO-8859-1',
});
}
sub logon
{
my ($cal) = @_;
my ($session);
$session =
Calendar::CSA::logon("",
{
'user_name' => $cal,
'user_type' => 'INDIVIDUAL',
'calendar_address' => $cal,
});
$session->short_entry_names(1);
$session->unix_times(1);
return $session;
}
sub getCreateDate
{
my ($session) = @_;
my ($date);
eval
{
$date =
$session->read_calendar_attributes->{"Date Created"}->{"value"};
};
if ($@)
{
PilotManager::msg("Error $@ getting calendar attributes");
return undef;
}
return $date;
}
sub logoff
{
my ($session) = @_;
eval
{
$session->logoff();
};
if ($@)
{
PilotMgr::msg("Error $@ while logging off of calendar");
}
}
sub isTimeless
{
my ($tick) = @_;
my (@time) = localtime($tick);
return ($time[2] == 3 && # 3:41 AM
$time[1] == 41 &&
$time[0] == 0);
}
sub makeTimeless
{
my ($tick) = @_;
my (@time) = localtime($tick);
@time[0, 1, 2] = (0, 41, 3); # 3:41 AM
return Time::Local::timelocal(@time);
}
sub cmToNeutral
{
my ($cm_ref, $begin, $end) = @_;
my (%appt, $cm_appt);
my ($tick);
%appt = ();
# Look up the appointment
#
my (@attr_list) = (
'Reference Identifier',
'Classification',
'Start Date',
'End Date',
'Repeat Times',
'Recurrence Rule',
'Exception Dates',
'Show Time',
'Summary'
);
if ($DEFAULTS->{"Alarm"}->{"on"})
{
push(@attr_list, "$DEFAULTS->{Alarm}->{value} Reminder");
}
$cm_appt = {$cm_ref->read_entry_attributes(@attr_list)};
# Sanity check 1: End before Start?
#
if ($cm_appt->{"End Date"}->{"value"} <
$cm_appt->{"Start Date"}->{"value"})
{
PilotMgr::msg("Start time after End time unsupported.\n" .
"Start time: " .
localtime($cm_appt->{"Start Date"}->{"value"}) . "\n" .
" End time: " .
localtime($cm_appt->{"End Date"}->{"value"}) .
"\nFixing that by setting End time to Start time\n");
$cm_appt->{"End Date"}->{"value"} = $cm_appt->{"Start Date"}->{"value"};
}
# Sanity check 2: End and Start on same day?
#
if ((localtime($cm_appt->{"Start Date"}->{"value"}))[7] !=
(localtime($cm_appt->{"End Date"}->{"value"}))[7])
{
croak("Start time and End time not on the same day\n" .
"Start time: " .
localtime($cm_appt->{"Start Date"}->{"value"}) . "\n" .
" End time: " .
localtime($cm_appt->{"End Date"}->{"value"}));
}
$appt{"cm_id"} = $cm_appt->{"Reference Identifier"}->{"value"};
if (&isTimeless($cm_appt->{"Start Date"}->{"value"}) ||
$cm_appt->{"Show Time"}->{"value"} == 0)
{
$appt{"timeless"} = 1;
$appt{"begin"} =
&SyncCM::timeops::startOfDay($cm_appt->{"Start Date"}->{"value"});
}
else
{
$appt{"timeless"} = 0;
$appt{"begin"} = $cm_appt->{"Start Date"}->{"value"};
$appt{"end"} = $cm_appt->{"End Date"}->{"value"};
}
if (exists($cm_appt->{"Repeat Times"}) &&
$cm_appt->{"Repeat Times"}->{"value"} == 0)
{
$appt{"repeat_forever"} = 1;
}
else
{
$appt{"repeat_forever"} = 0;
}
if (exists($cm_appt->{"Recurrence Rule"}))
{
my ($rule);
# Reverse sort the %NeutralRepeat keylist so that '.*'
# is at the end of the list.
#
foreach $rule (reverse sort keys %NeutralRepeat)
{
if ($cm_appt->{'Recurrence Rule'}->{'value'} =~ /$rule/)
{
my ($interval, $flags, $stop) = ($1, $2, $3);
# Don't forget to make a copy of the rule so that
# we don't modify the template rule itself later on.
#
$appt{"repeat"} = [@{$NeutralRepeat{$rule}}];
$appt{"repeat"}->[1] = $interval;
if ($rule =~ /^W/)
{
if ($flags !~ /\S/)
{
# Ex: "W1 #4"
#
$appt{"repeat"}->[2] =
1 << (localtime($appt{"begin"}))[6];
}
else
{
# Ex: "W1 MO TU WE #4"
#
$appt{"repeat"}->[2] = &daysToBits($flags);
}
}
if ($rule =~ /^MP/)
{
my (@tm) = localtime($appt{"begin"});
if ($flags !~ /\S/)
{
# Ex: MP1 #5
#
$appt{"repeat"}->[2] = &my_ceil($tm[3] / 7) - 1;
$appt{"repeat"}->[3] = 1 << $tm[6];
}
else
{
# Ex: MP1 1- FR #12
#
$flags =~ /\s*(\d+-)\s*(\w+)/;
my ($tag, $day) = ($1, $2);
if ($tag ne "1-")
{
# Uh, no idea what to do. Abort!
#
croak("Unknown repeat rule [$rule]");
}
else
{
# We're talking about the last week of the
# month here.
#
$appt{"repeat"}->[2] = 4; # Weeks are 0 based
if ($day !~ /\S/)
{
$appt{"repeat"}->[3] = 1 << $tm[6];
}
else
{
$appt{"repeat"}->[3] = &daysToBits($day);
}
}
}
}
if (defined($stop) && $stop =~ /\S/)
{
# It's possible to have a rule like
#
# W2 #0 19970910T215959Z
#
# Which means, repeat weekly forever but stop
# after 9/10/97. However, 9/10/97 may be out
# of our scope. So, turn off repeat_forever
#
$appt{"repeat_forever"} = 0;
}
if (!$appt{"repeat_forever"})
{
# Figure out the end of the sequence
#
my ($tick, %last, $tmp);
my ($seqlist, @seq);
# list_entry_sequence() is not start-point inclusive!
# So...back up 1 tick.
#
$seqlist = $cm_ref->list_entry_sequence(
[$cm_appt->{"Start Date"}->{"value"}-1, $end]);
if (!defined($seqlist))
{
# Hmm. This should never happen because we should
# always turn up the appointment we're starting with.
# Croak.
croak("internal error reading sequence information");
}
@seq = $seqlist->entries;
%last =
$seq[@seq-1]->read_entry_attributes("Start Date");
$tick = $last{"Start Date"}->{"value"};
if ($tick == $cm_appt->{"Start Date"}->{"value"})
{
# Ugh. This is one of a repeating sequence, but
# only this appointment falls within our scope (ie,
# it's first and last). Make this a non-repeating
# appointment.
$appt{"repeat"} = ["none", 0];
}
else
{
if ($appt{"timeless"})
{
$appt{"repeat_end"} =
&SyncCM::timeops::startOfDay($tick);
}
else
{
$appt{"repeat_end"} = $tick;
}
}
}
last;
}
}
}
else
{
$appt{"repeat"} = ["none", 0];
}
if (exists($cm_appt->{"Exception Dates"}))
{
my ($tmp);
foreach $tmp (@{$cm_appt->{"Exception Dates"}->{"value"}})
{
next unless defined ($tmp);
# We want to ignore any exceptions that occur after repeat_end.
# These exceptions are created to allow Calendar Manager to
# mimic repeat_end functionality (which may fall in the middle
# of a weekdays sequence) and shouldn't be mirrored in the
# neutral format.
#
if (exists($appt{"repeat_forever"}) && $appt{"repeat_forever"} ||
(exists($appt{"repeat_end"}) && $tmp <= $appt{"repeat_end"}))
{
push(@{$appt{"exceptions"}}, $tmp);
}
}
}
my ($text) = $cm_appt->{"Summary"}->{"value"};
chomp($text);
if ($text =~ /\n/)
{
($appt{"description"}, $appt{"note"}) =
split("\n", $text, 2);
}
else
{
$appt{"description"} = $text;
}
# If the user cares about privacy, map that here
#
if ($DEFAULTS->{"Privacy"}->{"on"} &&
$cm_appt->{"Classification"}->{"value"} ==
$PrivacyTable{$DEFAULTS->{"Privacy"}->{"mapping"}})
{
$appt{"private"} = 1;
}
else
{
$appt{"private"} = 0;
}
# And, if they are mapping an alarm, set that here.
#
if ($DEFAULTS->{"Alarm"}->{"on"})
{
my ($key) = "$DEFAULTS->{Alarm}->{value} Reminder";
if (defined($cm_appt->{$key}))
{
$appt{"alarm"} = $cm_appt->{$key}->{"value"}->{"lead_time"};
}
}
SyncCM::debug("CM converted %s\n\tto\n%s", $cm_appt, \%appt);
return \%appt;
}
sub neutralToCm
{
my ($appt) = @_;
my (%appt) = %$appt;
my ($cm_appt, $type);
$cm_appt->{"Subtype"} = &data('STRING', "Subtype Appointment");
if ($appt{"timeless"})
{
my ($tmp) = &makeTimeless($appt{"begin"});
# Cm seems to set the duration to be 1 minute for timeless events
#
$cm_appt->{"Start Date"} = &data('DATE TIME', $tmp);
$cm_appt->{"End Date"} = &data('DATE TIME', $tmp + 60);
$cm_appt->{"Show Time"} = &data('SINT32', 0);
}
else
{
$cm_appt->{"Start Date"} = &data('DATE TIME', $appt{"begin"});
$cm_appt->{"End Date"} = &data('DATE TIME', $appt{"end"});
$cm_appt->{"Show Time"} = &data('SINT32', 1);
}
if (exists($appt{"repeat"}) && $appt{"repeat"}->[0] ne "none")
{
my ($count) = &calcCount(%appt);
$type = $CmRepeat{$appt{"repeat"}->[0]};
$type =~ s/<x>/$appt{"repeat"}->[1]/;
$type =~ s/<y>/$count/;
if ($type =~ /<d>/)
{
my ($days) = &bitsToDays($appt{"repeat"}->[2]);
$type =~ s/<d>/$days/;
}
if ($type =~ /<f>/)
{
my ($buf);
# For the most part, we don't have to put anything in
# for the monthly-day flag field. However, in the case
# where we're repeating on the last week (as opposed to
# the 4th week) we need to specify a little extra.
#
$buf = "";
if ($appt{"repeat"}->[2] == 4)
{
# Last week of the month
#
$buf = "1- " . &bitsToDays($appt{"repeat"}->[3]);
}
$type =~ s/<f>/$buf/;
}
# Tag on an end date, for good measure
#
if (exists($appt{"repeat_end"}) && $appt{"repeat_end"})
{
my (@tmp) = localtime($appt{"repeat_end"});
if ($appt{"timeless"})
{
@tmp[0..2] = (0, 41, 3);
}
else
{
@tmp[0..2] = (localtime($appt{"begin"}))[0..2];
}
my ($tick) = Time::Local::timelocal(@tmp);
$type .= " " . &SyncCM::timeops::tickToISO($tick);
}
$cm_appt->{"Recurrence Rule"} = &data('STRING', $type);
if (exists $appt{"exceptions"} && @{$appt{"exceptions"}})
{
my ($excep) = ([@{$appt{"exceptions"}}]);
if ($appt{"timeless"})
{
grep($_ = &makeTimeless($_), @$excep);
}
$cm_appt->{"Exception Dates"} = &data('DATE TIME LIST', $excep);
}
}
my ($buf);
$buf = $appt{"description"};
if ($appt{"note"})
{
$buf .= "\n" . $appt{"note"};
}
$cm_appt->{"Summary"} = &data('STRING', $buf);
# If the user specifically made this appointment private,
# set that here.
#
if ($DEFAULTS->{"Privacy"}->{"on"} &&
defined($appt{"private"}) && $appt{"private"} == 1)
{
$cm_appt->{"Classification"} =
&data('UINT32',
$PrivacyTable{$DEFAULTS->{"Privacy"}->{"mapping"}});
}
# If the user wants to map the alarm, set that here also
#
if (defined($appt{"alarm"}) && $DEFAULTS->{"Alarm"}->{"on"})
{
my ($key) = "$DEFAULTS->{Alarm}->{value} Reminder";
$cm_appt->{$key}->{"type"} = "REMINDER";
$cm_appt->{$key}->{"value"}->{"lead_time"} = 0 + $appt{"alarm"};
}
SyncCM::debug("CM converted %s\n\tto\n%s", \%appt, $cm_appt);
return $cm_appt;
}
# Calculate the number of times an appointment repeats based
# upon the recurrence rule.
#
#
sub calcCount
{
my (%appt) = @_;
my (@lt) = localtime($appt{"begin"});
my ($total);
if ($appt{"repeat_forever"})
{
return 0;
}
if ($appt{"repeat"}[0] eq "daily")
{
my ($days);
# Total number of days divided by the interval.
#
$total = &SyncCM::timeops::diff_ndays($appt{"begin"},
$appt{"repeat_end"});
$total = $total / $appt{"repeat"}[1];
# Now, we're one short because we're not considering the
# appointment at the "begin" tick.
#
$total = int($total) + 1;
}
elsif ($appt{"repeat"}[0] eq "weekly")
{
# Number of weeks is computed by subtracting the first tick
# of the week that contains the start of the appointment from
# the repeat_end and dividing by the number of seconds in a week.
#
my ($tmp);
$tmp = &SyncCM::timeops::next_ndays($appt{"begin"}, -$lt[6]);
$tmp = &SyncCM::timeops::startOfDay($tmp);
# Now 'tmp' is the first tick of the week containing the
# first appt.
#
$total = &SyncCM::timeops::diff_ndays($tmp,
$appt{"repeat_end"});
# Then divide by the interval
#
$total = $total / (7 * $appt{"repeat"}[1]);
# Now, we're one short because we're not considering the
# appointment at the "begin" tick.
#
$total = int($total) + 1;
}
elsif ($appt{"repeat"}[0] eq "monthly-day")
{
my ($desired_dow) = &bitToIndex($appt{"repeat"}[3]);
my ($time) = $appt{"begin"};
$total = 0;
while ($time <= $appt{"repeat_end"})
{
my (@tmp) = localtime($time);
# Bump the month (and year, if necessary) up appropriately
#
$tmp[4] += $appt{"repeat"}[1];
if ($tmp[4] > 11)
{
$tmp[5] += int($tmp[4] / 12); # Bump the years
$tmp[4] = $tmp[4] % 12;
}
if ($appt{"repeat"}[2] == 4)
{
# Last X day of the month
# Figure out what day of the week the first day of
# the next month is and subtract back from there.
#
my (@tmp2) = @tmp;
$tmp2[4]++;
if ($tmp2[4] > 11)
{
$tmp2[5] += 1;
$tmp2[4] %= 12;
}
$tmp2[3] = 1;
my ($next_month_tick) = Time::Local::timelocal(@tmp2);
my ($dow) = (localtime($next_month_tick))[6];
my ($diff) = $dow - $desired_dow;
if ($diff <= 0)
{
$diff += 7;
}
# Now $diff represents the number of days back from
# the beginning of the next month. Count back from
# there to get the correct time
#
$time = &SyncCM::timeops::next_ndays($next_month_tick,
-1 * $diff);
}
else
{
# Nth X day of the month
#
# Start at the first day of the month
#
$tmp[3] = 1;
# Jump up (X / 7) weeks.
#
my ($base) = Time::Local::timelocal(@tmp);
$base = &SyncCM::timeops::next_ndays($base,
$appt{"repeat"}[2] * 7);
# Now advance to the desired day of the week
#
my ($dow) = (localtime($base))[6];
my ($diff) = $desired_dow - $dow;
if ($diff < 0)
{
$diff += 7;
}
# Skip up $diff days
#
$time = &SyncCM::timeops::next_ndays($base, $diff);
}
$total++;
}
}
elsif ($appt{"repeat"}[0] eq "monthly-date")
{
my ($time) = $appt{"begin"};
$total = 0;
while ($time <= $appt{"repeat_end"})
{
my (@tmp) = localtime($time);
$tmp[4] += $appt{"repeat"}[1];
if ($tmp[4] > 11)
{
$tmp[5] += int($tmp[4] / 12); # Bump the years
$tmp[4] = $tmp[4] % 12;
}
$time = Time::Local::timelocal(@tmp);
$total++;
}
}
elsif ($appt{"repeat"}[0] eq "yearly")
{
my ($time) = $appt{"begin"};
$total = 0;
while ($time <= $appt{"repeat_end"})
{
my (@tmp) = localtime($time);
$tmp[5]++;
$time = Time::Local::timelocal(@tmp);
$total++;
}
}
else
{
PilotMgr::msg("Unknown repeat type: $appt{repeat}[0]");
return 1;
}
return $total;
}
sub daysToBits
{
my ($days) = @_;
my ($key);
my (%hash) =
(
'SU' => 1 << 0,
'MO' => 1 << 1,
'TU' => 1 << 2,
'WE' => 1 << 3,
'TH' => 1 << 4,
'FR' => 1 << 5,
'SA' => 1 << 6,
);
my ($ret) = 0;
foreach $key (keys %hash)
{
if ($days =~ /$key/)
{
$ret += $hash{$key};
}
}
return $ret;
}
sub bitsToDays
{
my ($val) = @_;
my ($i, @result);
my (@days) = ('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
for ($i = 0; $i < 7; $i++)
{
push (@result, $days[$i])
if ($val & (1 << $i));
}
return join(" ", @result);
}
sub data
{
my ($type, $value) = @_;
# Make sure that the integer values aren't represented
# as a string. If they go to the Calendar::CSA layer as
# strings, CSA assumes that they are in CSA's ISO format
# and won't convert them.
#
if ($type =~ /^(DATE TIME|SINT32|UINT32|)$/)
{
$value += 0;
}
return ({
'type' => $type,
'value' => $value,
});
}
sub emptyCalendar
{
my ($session, $name) = @_;
my ($begin, $end, $stop, $start);
$begin = 8 * 60 * 60; # Thu Jan 1 0:00:00 PST8PDT 1970
$end = 1262332800; # Fri Jan 1 0:00:00 PST8PDT 2010
$start = $begin;
while ($start < $end)
{
SyncCM::status("Erasing $name",
&fake_ceil(75 * ($start - $begin) / ($end - $begin)));
$stop = $start + 16 * 7 * 24 * 60 * 60;
$stop = $end if ($stop > $end);
my (@attributes) = (
'Start Date' =>
{
type => 'DATE TIME',
value => &SyncCM::timeops::tickToISO($begin),
match => 'GREATER THAN OR EQUAL TO',
},
'Start Date' => {
type => 'DATE TIME',
value => &SyncCM::timeops::tickToISO($stop),
match => 'LESS THAN OR EQUAL TO',
});
my($entrylist) = $session->list_entries(@attributes);
next unless $entrylist;
foreach (reverse $entrylist->entries)
{
eval
{
$_->delete_entry("ALL");
};
}
}
continue
{
$start = $stop + 1;
}
SyncCM::status("Erasing $name", 75);
}
sub loadAppointmentsAll
{
my ($session) = @_;
my ($begin, $end);
# This is retarded. What I should really be doing here is
# not defining the Start/End dates and letting the implementation
# pull out all records. However, the Solaris implementation
# appears to do wacky things if you don't at least give it a
# start and end date.
#
# If this conduit is still around in 2010, we'll have bigger
# fish to fry :-)
#
$begin = 8 * 60 * 60; # Thu Jan 1 0:00:00 PST8PDT 1970
$end = 1262332800; # Fri Jan 1 0:00:00 PST8PDT 2010
return &loadAppointmentsRange($session, $begin, $end);
}
sub loadAppointmentsRange
{
my ($session, $begin, $end) = @_;
my (@attributes);
my (%seen);
my ($db);
# Break our range down into chunks and load each chunk
# at a time. This will tread lighter on the implementation
# which may get overloaded by too large a request.
#
my ($start, $stop, $chunk);
$chunk = 16 * 7 * 24 * 60 * 60; # 16 weeks.
$start = $begin;
$stop = $begin + $chunk;
if ($stop > $end)
{
$stop = $end;
}
while ($start < $end)
{
SyncCM::checkCancel();
SyncCM::status("Loading Calendar Manager appointments",
&fake_ceil(100 * ($start - $begin) / ($end - $begin)));
my (@attributes) = (
'Start Date' =>
{
type => 'DATE TIME',
value => &SyncCM::timeops::tickToISO($start),
match => 'GREATER THAN OR EQUAL TO',
},
'Start Date' => {
type => 'DATE TIME',
value => &SyncCM::timeops::tickToISO($stop),
match => 'LESS THAN OR EQUAL TO',
});
my ($ref, $entrylist, @refs);
$entrylist = $session->list_entries(@attributes);
next unless $entrylist;
@refs = $entrylist->entries;
foreach $ref (@refs)
{
my ($attrs);
$attrs = {$ref->read_entry_attributes("Reference Identifier",
"Summary")};
# Assuming that the appointments arrive in
# chronological order, we only need to process
# the first one. Yes, this is implementation
# dependant.
#
# XXX: fix this
#
next if $seen{$attrs->{"Reference Identifier"}->{"value"}}++;
eval
{
my ($entry) = &cmToNeutral($ref, $begin, $end);
$db->{$attrs->{"Reference Identifier"}->{"value"}} = $entry;
};
if ($@)
{
$@ =~ s/X#X.*//;
my ($summary, $date);
$attrs = {$ref->read_entry_attributes("Reference Identifier",
"Summary",
"Start Date")};
$summary = (split(/\n/, $attrs->{Summary}->{value}, 2))[0];
$date = localtime($attrs->{"Start Date"}->{value});
PilotMgr::msg("Desktop appointment $summary / $date " .
"is unsupported\n$@");
}
}
}
continue
{
$start = $stop + 1;
$stop += $chunk;
if ($stop > $end)
{
$stop = $end;
}
}
return $db;
}
# This is great...except it can't tell you about the appointments
# that have been deleted, so we can't use it. :-(
#
sub loadAppointmentsModified
{
my ($session, $since) = @_;
return &loadAppointments($session,
'Last Update' =>
{
type => 'DATE TIME',
value => &tickToISO($since),
match => 'GREATER THAN OR EQUAL TO',
});
}
sub createAppt
{
my ($session, $appt) = @_;
my ($cm_appt) = &neutralToCm($appt);
my ($new_appt, $id);
# Set the type. This can only be done when the appointment
# is created, so we don't do it in neutralToCm().
#
$cm_appt->{"Type"} = &data('UINT32', 0);
# This is a new appointment, so add in our defaults
#
my ($key);
my (%conversion) =
(
"minutes" => 60,
"hours" => 60 * 60,
"days" => 24 * 60 * 60
);
foreach $key ("Audio", "Flashing", "Popup", "Mail")
{
next unless $DEFAULTS->{$key}->{"on"};
# If we're mapping an alarm, then don't overwrite
# the reminder that the alarm is mapped to.
#
next if ($DEFAULTS->{"Alarm"}->{"on"} &&
$key eq $DEFAULTS->{"Alarm"}->{"value"});
$cm_appt->{"$key Reminder"}->{"type"} = 'REMINDER';
$cm_appt->{"$key Reminder"}->{"value"}->{"lead_time"} =
$DEFAULTS->{$key}->{"value"} *
$conversion{$DEFAULTS->{$key}->{"units"}};
if ($DEFAULTS->{$key}->{"data"})
{
$cm_appt->{"$key Reminder"}->{"value"}->{"data"} =
$DEFAULTS->{$key}->{"data"};
}
}
# Add in default privacy for new appointments, unless it's
# already set (which means that the user specified it in the
# 'private' flag).
if (!defined($cm_appt->{"Classification"}))
{
if (defined($DEFAULTS->{"Privacy"}))
{
$cm_appt->{"Classification"} =
&data('UINT32',
$PrivacyTable{$DEFAULTS->{"Privacy"}->{"default"}});
}
}
eval
{
$new_appt = $session->add_entry(%$cm_appt);
my (%entry) = $new_appt->read_entry_attributes("Reference Identifier");
$id = $entry{"Reference Identifier"}->{"value"};
};
if ($@)
{
$@ =~ s/ at.*$//;
chomp($@);
PilotMgr::msg("Error $@ while adding $appt->{description}");
SyncCM::error("Desktop error adding: %s\n", SyncCM::dump($cm_appt));
return undef;
}
return $id;
}
sub changeAppt
{
my ($session, $orig_appt, $appt) = @_;
my ($newid, $entrylist);
SyncCM::debug("CM: changing %s to %s", $orig_appt, $appt);
$entrylist = &findEntry($session, $orig_appt);
if (!$entrylist)
{
PilotMgr::msg("Unable to change '$orig_appt->{description}' [$@]");
return -1;
}
my ($cm_appt) = &neutralToCm($appt);
eval
{
my ($entry) = $entrylist->entries;
$entry->update_entry_attributes("ALL", 0, %$cm_appt);
my (%attrs) = $entry->read_entry_attributes();
$newid = $attrs{"Reference Identifier"}->{"value"};
};
if ($@)
{
PilotMgr::msg("Unable to change '$orig_appt->{description}' [$@]");
return undef;
}
return $newid;
}
sub findEntry
{
my ($session, $appt) = @_;
my ($entrylist);
SyncCM::debug("Searching for %s", $appt);
eval
{
$entrylist =
$session->list_entries('Reference Identifier' =>
{
type => 'OPAQUE DATA',
value => $appt->{"cm_id"},
match => 'EQUAL TO',
});
};
if ($@)
{
PilotMgr::msg("findEntry Error $@");
return undef;
}
SyncCM::debug("found %s", $entrylist);
return $entrylist;
}
sub deleteAppt
{
my ($session, $appt) = @_;
my ($entrylist);
SyncCM::debug("CM: deleting: %s", $appt);
eval
{
$entrylist = &findEntry($session, $appt);
croak "Couldn't find entry"
unless $entrylist;
($entrylist->entries)[0]->delete_entry("ALL");
};
if ($@)
{
PilotMgr::msg("deleteAppt Error $@");
return -1;
}
return 0;
}
sub bitcount
{
my ($i);
my ($total) = 0;
$total = 0;
foreach $i (split(//, unpack("B*", pack("i", $_[0]))))
{
$total += $i;
}
return $total;
}
sub bitToIndex
{
my ($val) = @_;
my ($ret) = 0;
while ($val / 2 >= 1)
{
$val /= 2;
$ret++;
}
return $ret;
}
# POSIX's ceil is hosed on some systems
#
sub fake_ceil
{
my ($val) = int($_[0]);
return 1 if ($val == 0);
return $val;
}
# POSIX's ceil is hosed on some systems
#
sub my_ceil
{
my ($val) = $_[0];
if ($val > int($val))
{
return int($val) + 1;
}
return int($val);
}
1;
228c228
< PilotMgr::msg("Start time after End time unsupported.\n" .
---
> croak("Start time after End time\n" .
232,234c232
< localtime($cm_appt->{"End Date"}->{"value"}) .
< "\nFixing that by setting End time to Start time\n");
< $cm_appt->{"End Date"}->{"value"} = $cm_appt->{"Start Date"}->{"value"};
---
> localtime($cm_appt->{"End Date"}->{"value"}) . "X#X");