2
print $Config{startperl}, "\n";
14
my ($controldir, $dlp, $info, $db, $port);
15
my (%control, %pilothash, %pilotID, %planID, %exceptID, %planRecord,
17
my ($slowsync, $file, $pilotname, $maxseed, $netplanversion);
24
my @plversion; # pilot-link version (version, major, minor, patch)
26
# any or alll of these may be undefined, depending on the
29
$plversion[0] = PDA::Pilot::PILOT_LINK_VERSION();
30
$plversion[1] = PDA::Pilot::PILOT_LINK_MAJOR();
31
$plversion[2] = PDA::Pilot::PILOT_LINK_MINOR();
32
$plversion[3] = PDA::Pilot::PILOT_LINK_PATCH();
35
# msg and status are here to localize the differences between the
36
# standalone sync-plan.PL and the SyncPlan.pm module for PilotManager.
38
############################################################
40
############################################################
48
############################################################
49
# CheckErrNotFound: Argument is a PDA::Pilot::DLP or a
50
# PDA::Pilot::DLP::DB. It's in its own package so that croak will
51
# give more useful information. I'm not using the equivalent function
52
# from the PilotMgr package because there is a stand-alone version of
53
# this conduit in the pilot-link distribution.
54
############################################################
61
my $errno = $obj->errno();
62
if (defined $plversion[0]) { # pilot-link version is >= 0.12.0-pre2
63
if ($errno != PDA::Pilot::PI_ERR_DLP_PALMOS()) {
66
if (($errno = $obj->palmos_errno()) != PDA::Pilot::dlpErrNotFound()) {
67
croak "Error $errno: " . PDA::Pilot::errorText($errno);
70
croak "Error $errno" if ($errno != -5); # dlpErrNotFound
74
*checkErrNotFound = \&ErrorCheck::checkErrNotFound;
77
############################################################
79
############################################################
82
my ($m,$d,$y) = split(m!/!,$PlanDate);
91
timegm(0,0,0,$d,$m,$y);
94
############################################################
96
############################################################
99
my ($h,$m,$s) = split(m!:!,$PlanTime);
101
return undef if $h == 99 and $m == 99 and $s == 99;
103
$s + ($m * 60) + ($h * 60 * 60);
106
############################################################
108
############################################################
111
return "99:99:99" if not defined $PerlDT;
113
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
119
############################################################
121
############################################################
122
sub TimeRelPerlToPlan {
124
return "99:99:99" if not defined $PerlDT;
126
my ($sec,$min,$hour);
128
$hour = int($PerlDT/ (60*60));
129
$PerlDT -= $hour*60*60;
131
$min = int($PerlDT/ (60));
140
############################################################
142
############################################################
143
sub DatePilotToPerl {
144
my ($s,$m,$h, $mday,$mon,$year) = @_;
146
if (ref $s eq 'ARRAY') {
147
($s,$m,$h, $mday,$mon,$year) = @$s;
151
if ($year >= 70 and $year <= 138) {
152
$date = eval { timegm($s,$m,$h,$mday,$mon,$year) };
153
msg("Trouble converting date: $mon/$mday/$year $h:$m$s")
155
$time = $s + 60 * ($m + 60 * $h);
158
msg("Bad year: $year");
161
return wantarray ? ($date, $time) : $date;
164
############################################################
166
############################################################
169
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
178
############################################################
180
############################################################
181
sub RecordPlanToPilot {
182
my ($plan,$pilot) = @_;
183
if (not defined $pilot) {
184
$pilot = PDA::Pilot::AppointmentDatabase->record;
187
$pilot->{'id'} = $plan->{'pilotid'};
188
$pilot->{'description'} = join("\xA", @{$plan->{'note'}}) if defined $plan->{'note'};
189
$pilot->{'note'} = join("\xA", @{$plan->{'message'}}) if defined $plan->{'message'};
190
$pilot->{'description'} ||= "";
192
if (defined $plan->{'time'}) {
193
$pilot->{'begin'} = [gmtime($plan->{'date'}+$plan->{'time'})];
194
$pilot->{'end'} = [gmtime($plan->{'date'}+$plan->{'time'}+$plan->{'length'})];
197
$pilot->{'begin'} = [gmtime($plan->{'date'})];
198
$pilot->{'event'} = 1;
199
$plan->{'early'} = 0;
203
if ($plan->{'early'} and $plan->{'late'} and ($plan->{'early'} != $plan->{'late'})) {
204
msg( "Two alarms - using earlier one." );
205
$plan->{'late'} = $plan->{'early'};
207
if ($plan->{'early'} or $plan->{'late'}) {
208
my ($alarm) = $plan->{'early'} || $plan->{'late'};
209
if ($alarm > (60*60*24)) {
210
$pilot->{'alarm'}->{'units'} = "days";
211
$pilot->{'alarm'}->{'advance'} = int($alarm / (60*60*24));
212
} elsif ($alarm > (60*60)) {
213
$pilot->{'alarm'}->{'units'} = "hours";
214
$pilot->{'alarm'}->{'advance'} = int($alarm / (60*60));
216
$pilot->{'alarm'}->{'units'} = "minutes";
217
$pilot->{'alarm'}->{'advance'} = int($alarm / 60);
221
if (defined $plan->{'exceptions'}) {
222
foreach (@{$plan->{'exceptions'}}) {
223
push @{$pilot->{'exceptions'}}, [gmtime($_)];
226
delete $pilot->{'exceptions'};
229
if (defined $plan->{'repeat'}) {
230
msg( "Converting repetition...\n" ) if ($PREFS->{'Debug'} > 2);
231
delete $pilot->{'repeat'};
232
if ($plan->{'repeat'}->[1]) {
233
$pilot->{'repeat'}->{'end'} = [gmtime($plan->{'repeat'}->[1])];
235
my ($days,$end,$weekday,$mday,$yearly) = @{$plan->{'repeat'}};
236
msg( "Days: $days, End: $end, Weekday: $weekday, Mday: $mday, Yearly: $yearly\n" ) if ($PREFS->{'Debug'} > 2);
237
$pilot->{'repeat'}->{'weekstart'} = 0;
238
$pilot->{'repeat'}->{'frequency'} = 1;
239
if ($days and !$weekday and !$mday and !$yearly) {
240
$pilot->{'repeat'}->{'type'} = "Daily";
241
$pilot->{'repeat'}->{'frequency'} = $days / (60*60*24);
242
} elsif(!$days and !$weekday and !$mday and $yearly) {
243
$pilot->{'repeat'}->{'type'} = "Yearly";
244
} elsif(!$days and !$weekday and ($mday == (1 << $pilot->{'begin'}[3])) and !$yearly) {
245
$pilot->{'repeat'}->{'type'} = "MonthlyByDate";
247
} elsif(!$days and $weekday and (($weekday & 0xff80) == 0) and !$mday and !$yearly) {
248
$pilot->{'repeat'}->{'type'} = "Weekly";
249
foreach my $i (0..6) {
250
$pilot->{'repeat'}->{'days'}[$i] = !! ($weekday & (1<<$i));
252
# If the weekday list does include the day the event is one, abort
253
if (!$pilot->{'repeat'}{'days'}[$pilot->{'begin'}[6]]) {
256
} elsif(not $days and $weekday and not $mday and not $yearly) {
257
my ($wday) = $pilot->{'begin'}[6];
258
my ($week) = int(($pilot->{'begin'}[3]-1)/7);
259
msg( "weekday = $weekday, wday = $wday, week = $week\n" ) if ($PREFS->{'Debug'} > 2);
260
if (($weekday & 0x7f) != (1<<$wday)) {
263
if (($weekday & 4096) and ($weekday & 8192)) {
269
if (($weekday & 0xff00) != (256<<$week)) {
276
$pilot->{'repeat'}->{'type'} = "MonthlyByDay";
277
$pilot->{'repeat'}->{'day'} = $week*7+$wday;
282
delete $pilot->{'repeat'};
288
############################################################
290
############################################################
291
sub RecordPilotToPlan {
292
my ($pilot,$plan) = @_;
293
$plan = {color => 0} if not defined $plan;
295
$plan->{'pilotid'} = $pilot->{'id'};
297
$plan->{'message'} = [split("\xA", $pilot->{'note'})] if defined $pilot->{'note'};
298
$plan->{'note'} = [split("\xA", $pilot->{'description'})] if defined $pilot->{'description'};
300
my ($date, $time) = DatePilotToPerl($pilot->{'begin'});
302
msg("Begin time in Palm record untranslatable.");
306
$plan->{'date'} = $date;
307
if ($pilot->{'event'}) {
308
$plan->{'time'} = undef;
309
$plan->{'length'} = 0;
311
$plan->{'time'} = $time;
312
my $end = DatePilotToPerl($pilot->{'end'});
314
msg("End time in Palm record untranslatable.");
317
$plan->{'length'} = $end - $date;
320
if (exists $pilot->{'alarm'}) {
322
if ($pilot->{'alarm'}{'units'} eq "days") {
323
$alarm = $pilot->{'alarm'}->{'advance'} * (60*60*24);
324
} elsif ($pilot->{'alarm'}{'units'} eq "hours") {
325
$alarm = $pilot->{'alarm'}->{'advance'} * (60*60);
326
} elsif ($pilot->{'alarm'}{'units'} eq "minutes") {
327
$alarm = $pilot->{'alarm'}->{'advance'} * (60);
329
if ($plan->{'late'}) {
330
$plan->{'late'} = $alarm;
331
$plan->{'early'} = 0;
334
$plan->{'early'} = $alarm;
341
if (exists $pilot->{'exceptions'}) {
342
# Plan records can only deal with four exceptions,
343
if (@{$pilot->{'exceptions'}} > 4) {
344
msg("Too many exceptions.");
347
foreach (@{$pilot->{'exceptions'}}) {
348
push @{$plan->{'exceptions'}}, timegm(@{$_});
352
delete $plan->{'repeat'};
354
if (exists $pilot->{'repeat'}) {
355
$plan->{'repeat'} = [0,0,0,0,0];
356
if ($pilot->{'repeat'}->{'type'} eq "Daily") {
357
$plan->{'repeat'}->[0] = (60*60*24) * $pilot->{'repeat'}->{'frequency'};
358
$plan->{'repeat'}->[4] = 0;
359
} elsif ($pilot->{'repeat'}->{'type'} eq "Yearly" and ($pilot->{'repeat'}->{'frequency'}==1)) {
360
$plan->{'repeat'}->[4] = 1;
362
} elsif ($pilot->{'repeat'}->{'type'} eq "Weekly" and ($pilot->{'repeat'}->{'frequency'}==1)) {
364
foreach my $i (0..6) {
365
if ($pilot->{'repeat'}->{'days'}[$i]) {
369
$plan->{'repeat'}->[2] = $r;
370
} elsif ($pilot->{'repeat'}->{'type'} eq "Weekly" and ($pilot->{'repeat'}->{'frequency'}>1)) {
371
# Weekly repeat, not every week. If it repeats only once per week, convert it to a daily
372
# repeat with frequency a multiple of 7. If it repeats more than once a week, bail.
374
foreach my $i (0..6) {
375
$count ++ if ($pilot->{repeat}->{days}[$i]);
378
$plan->{'repeat'}->[0] = (60*60*24) * $pilot->{'repeat'}->{'frequency'} * 7;
379
$plan->{'repeat'}->[4] = 0;
381
msg("Repeat pattern too complex.");
384
} elsif ($pilot->{'repeat'}->{'type'} eq "MonthlyByDate" and ($pilot->{'repeat'}->{'frequency'}==1)) {
385
$plan->{'repeat'}->[3] = 1 << $pilot->{'begin'}[3];
386
} elsif ($pilot->{'repeat'}->{'type'} eq "MonthlyByDay" and ($pilot->{'repeat'}->{'frequency'}==1)) {
387
my ($day) = $pilot->{'repeat'}{'day'} % 7;
388
my ($week) = int($pilot->{'repeat'}{'day'} / 7);
389
$week = 5 if $week == 4;
390
$plan->{'repeat'}->[2] = (1 << $day) | (256 << $week);
392
msg("Repeat pattern too complex.");
395
if (defined $pilot->{'repeat'}->{'end'}) {
396
$plan->{'repeat'}->[1] = timegm(@{$pilot->{'repeat'}->{'end'}});
403
############################################################
405
############################################################
410
#print "Generating Plan record: ", Dumper($rec),"\n";
412
push(@output, DatePerlToPlan($rec->{'date'})." ".
413
TimeRelPerlToPlan($rec->{'time'})." ".
414
TimeRelPerlToPlan($rec->{'length'})." ".
415
TimeRelPerlToPlan($rec->{'early'})." ".
416
TimeRelPerlToPlan($rec->{'late'})." ".
417
($rec->{'suspended'} ? "S" : "-").
418
($rec->{'private'} ? "P" : "-").
419
($rec->{'noalarm'} ? "N" : "-").
420
($rec->{'hide_month'} ? "M" : "-").
421
($rec->{'hide_year'} ? "Y" : "-").
422
($rec->{'hide_week'} ? "W" : "-").
423
($rec->{'hide_yearover'} ? "O" : "-").
424
($rec->{'d_flag'} ? "D" : "-").
427
" ".$rec->{'color'});
429
if (defined $rec->{'repeat'}) {
430
push @output, "R\t".join(" ",@{$rec->{'repeat'}});
432
if (defined $rec->{'exceptions'}) {
433
foreach (@{$rec->{'exceptions'}}) {
434
push @output, "E\t".DatePerlToPlan($_);
437
if (defined $rec->{'note'}) {
438
push @output, map("N\t$_", @{$rec->{'note'}});
440
if (defined $rec->{'message'}) {
441
push @output, map("M\t$_", @{$rec->{'message'}});
443
if (defined $rec->{'script'}) {
444
push @output, map("S\t$_", @{$rec->{'script'}});
446
if (defined $rec->{'other'}) {
447
foreach (@{$rec->{'other'}}) {
452
my ($hash) = new Digest::MD5;
454
#print "Adding |$_| to hash\n";
457
$rec->{'pilothash'} = $hash->hexdigest;
460
for ($i=0;$i<@output;$i++) {
461
last if $output[$i] =~ /^S/;
463
$rec->{'pilotexcept'} += 0;
465
@US = @{$rec->{'unhashedscript'}} if defined $rec->{'unhashedscript'};
466
unshift @US, "S\t#Pilot: 1 $pilotname $rec->{'pilothash'} $rec->{'pilotexcept'} $rec->{'pilotid'}";
467
splice @output, $i, 0, @US;
470
msg( "Generated record |" . join("\n", @output). "|\n" ) if ($PREFS->{'Debug'} > 2);
475
############################################################
477
############################################################
478
sub PrintPlanRecord {
482
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
483
gmtime($rec->{'date'});
486
$output = "$year/$mon/$mday";
488
if ($rec->{'time'}) {
489
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
490
gmtime($rec->{'time'});
491
$output .= sprintf(" %02d:%02d-", $hour, $min);
493
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
494
gmtime($rec->{'time'}+$rec->{'length'});
495
$output .= sprintf("%02d:%02d", $hour, $min);
497
$output .= " '".join("\\n",@{$rec->{'note'}})."'" if defined $rec->{'note'};
498
$output .= " (".join("\\n",@{$rec->{'message'}}).")" if defined $rec->{'message'};
500
if ($rec->{'repeat'}) {
502
if ($rec->{'repeat'}[0]) {
503
push @r, "every " . ($rec->{'repeat'}[0] / (60*60*24)) . " days";
506
if ($rec->{'repeat'}[4]) {
507
push @r, "every year";
509
if ($rec->{'repeat'}[3]) {
510
my ($i) = $rec->{'repeat'}[3];
512
push @r, "the last day of each month";
515
push @r, "the $_ of each month" if $i & (1<<$_);
518
if ($rec->{'repeat'}[2]) {
519
push @r, "until ".scalar(gmtime($rec->{'repeat'}[2]));
522
$output .= " repeat ".join(", ", @r);
526
# $output .= " {ID:$rec->{'pilotid'}, Except:";
527
# $output .= $rec->{'pilotexcept'} if (defined $rec->{'pilotexcept'});
528
# $output .= ", Changed:";
529
# $output .= $rec->{'modified'} if (defined $rec->{'modified'});
530
# $output .= ", Deleted:";
531
# $output .= $rec->{'deleted'} if (defined $rec->{'deleted'});
537
############################################################
539
############################################################
540
sub PrintPilotRecord {
544
$output = ($rec->{'begin'}[5]+1900)."/".($rec->{'begin'}[4]+1)."/".$rec->{'begin'}[3];
546
if (!$rec->{'event'}) {
548
$output .= sprintf("%02d:%02d-%02d:%02d",
555
$output .= " '$rec->{'description'}'";
556
$output .= " ($rec->{'message'})" if (defined $rec->{'message'});
558
# $output .= " {ID:$rec->{'id'}, Except:";
559
# $output .= $exceptID{$rec->{'id'}} if (defined $exceptID{$rec->{'id'}});
560
# $output .= ", Changed:";
561
# $output .= $rec->{'modified'} if (defined $rec->{'modified'});
562
# $output .= ", Deleted:";
563
# $output .= $rec->{'deleted'} if (defined $rec->{'deleted'});
566
$output =~ s/\r/\\r/g;
567
$output =~ s/\n/\\n/g;
572
############################################################
574
# Takes a Plan record in hash format
576
############################################################
577
sub WritePlanRecord {
578
my ($socket, $record) = @_;
579
my ($raw) = generaterecord($record);
581
$record->{'id'} ||= 0;
582
#print "ID is $record->{'id'}\n";
584
$raw = "w$file $record->{'id'} $raw\n";
585
$record->{'raw'} = $raw;
586
SendPlanCommand($socket, $raw);
587
$reply = ReadPlanReply($socket);
588
#print "Installing record $record->{'id'} (PilotID: $record->{'pilotid'}) in Plan: ", Dumper($record);
589
# syswrite $socket, $raw, length($raw);
590
# sysread $socket, $reply, 1024;
591
# print "Reply to installation: |$reply|\n";
592
if ($reply =~ /^w[tf](\d+)/) {
593
$record->{'id'} = $1;
594
$planRecord{$1} = $record;
595
# print "New record id: $1\n";
597
msg( "Failed write: $reply\n" );
602
############################################################
604
############################################################
605
sub LoadPilotRecord {
607
my ($record) = $db->getRecord($i);
609
$pilotID{$record->{'id'}} = $record;
611
checkErrNotFound($db);
616
############################################################
618
# takes a Plan record in hash format
620
############################################################
621
sub DeletePlanRecord {
622
my ($socket, $record) = @_;
624
$raw = "d$file $record->{'id'}\n";
625
# print "Deleting record $record->{'id'} (PilotID: $record->{'pilotid'}) in Plan\n";
626
# syswrite $socket, $raw, length($raw);
627
SendPlanCommand($socket, $raw);
630
############################################################
632
# takes a Palm record in hash format
634
############################################################
635
sub WritePilotRecord {
636
my ($db, $control, $record) = @_;
638
$record->{'id'} ||= 0;
639
$record->{'category'} ||= 0;
641
#print "Installing record in Palm: ",Dumper($record);
643
my ($id) = $db->setRecord($record);
646
$pilotID{$id} = $record;
647
my ($hash) = HashPilotRecord($record);
648
$pilothash{$id} = $hash;
649
$dbname{$id} = $control->{'name'};
650
$record->{'id'} = $id;
657
############################################################
659
############################################################
660
sub DeletePilotRecord {
662
my ($result) = $db->deleteRecord($id);
664
delete $pilothash{$id};
665
delete $pilotID{$id};
667
delete $exceptID{$id};
675
############################################################
677
############################################################
679
my ($db,$socket,$control, $i,$r) = @_;
680
# print "Record: $r\n";
681
my (@l) = split(/\n/,$r);
682
my ($rec) = { raw => [@l], other => [] };
683
my (@E,@R,@N,@M,@S,@US);
684
my ($hash) = new Digest::MD5;
687
my ($date, $time, $length, $early, $late, $flags, $color) = split(/\s+/, shift @l);
688
$rec->{'pilotrec'} = "";
698
if ($s =~ /^\s*#Pilot:\s+(\d+)\s*(.*)$/) {
699
if ($1 == 1) { # version number
700
my ($name,$hash,$except,$id) = split(/\s+/, $2);
701
#print Dumper({Name=>$name,Hash=>$hash,Except=>$except,ID=>$id});
702
if ($name eq $pilotname) {
703
$rec->{'pilotid'} = $id;
704
$rec->{'pilotexcept'} = $except || 0;
705
$rec->{'pilothash'} = $hash;
711
next; # skip hash add
718
$rec->{'repeat'} = [split(/\s+/, $r)];
720
push @{$rec->{'other'}}, $_;
722
#print "Adding |$_| to hash\n";
725
$hash = $hash->hexdigest;
726
#print "Old hash: $hash, New hash: $rec->{'pilothash'}\n";
727
$rec->{'modified'} = (!defined($rec->{'pilothash'}) ||
728
($rec->{'pilothash'} ne $hash));
729
$rec->{'note'} = \@N if @N;
730
$rec->{'script'} = \@S if @S;
731
$rec->{'unhashedscript'} = \@US if @US;
732
$rec->{'message'} = \@M if @M;
733
$rec->{'date'} = DatePlanToPerl($date);
734
$rec->{'time'} = TimePlanToPerl($time);
735
$rec->{'length'} = TimePlanToPerl($length);
736
$rec->{'early'} = TimePlanToPerl($early);
737
$rec->{'late'} = TimePlanToPerl($late);
738
$rec->{'color'} = $color;
740
$rec->{'suspended'} = substr($flags,0,1) ne "-";
741
$rec->{'private'} = substr($flags,1,1) ne "-";
742
$rec->{'noalarm'} = substr($flags,2,1) ne "-";
743
$rec->{'hide_month'} = substr($flags,3,1) ne "-";
744
$rec->{'hide_year'} = substr($flags,4,1) ne "-";
745
$rec->{'hide_week'} = substr($flags,5,1) ne "-";
746
$rec->{'hide_yearover'} = substr($flags,6,1) ne "-";
747
$rec->{'d_flag'} = substr($flags,7,1) ne "-";
748
$rec->{'locked'} = 1;
751
$rec->{'exceptions'} = [map(DatePlanToPerl($_), @E)] if @E;
753
$planRecord{$i} = $rec;
755
#print "Read plan record:\n";
759
############################################################
761
############################################################
762
sub HashPilotRecord {
764
my ($hash) = new Digest::MD5;
765
$hash->add($record->{'raw'});
770
############################################################
772
############################################################
774
my ($db,$socket,$control) = @_;
775
msg( "After stuff:\n" ) if ($PREFS->{'Debug'} > 2);
777
##################################################################
778
# This batch of code scans for Plan records with identical Pilot
779
# IDs, presumambly caused by duplicating a plan record. We remove
780
# the ids from the duplicates. The weird sort is magic to prefer
781
# keeping the id (and thus leaving unmodified) of an otherwise
783
##################################################################
785
my (@uniq) = sort {$a->{'pilotid'} <=> $b->{'pilotid'} or $a->{'modified'} <=> $b->{'modified'}} grep {exists $_->{'pilotid'}} values %planRecord;
787
for($i=@uniq-1;$i>=1;$i--) {
788
#print "Checking plan record: ", Dumper($uniq[$i]),"\n";
789
if ($uniq[$i]->{'pilotid'} == $uniq[$i-1]->{'pilotid'}) {
790
delete $uniq[$i]->{'pilotid'};
791
$planID{$uniq[$i-1]->{'pilotid'}} = $uniq[$i-1];
792
#print "... A dup, blessed be ye without id, and be ye modified.\n";
793
$uniq[$i]->{'modified'} = 1;
797
######################################################################
798
# Use our saved Pilot ID cache to detect deleted Plan records. This
799
# will not catch deleted Plan records that were never assigned a
800
# Pilot ID, but that is OK because such records do not have to be
801
# removed from the Palm.
802
######################################################################
803
my ($loop_count) = (0);
806
foreach (keys %pilothash) {
808
# Palm records originally downloaded from a different Plan database
809
# are off-limits during this pass.
811
next if $dbname{$_} ne $control->{'name'};
814
# print "Palm cached ID: $_\n";
815
if (not defined $planID{$_} and not $exceptID{$_}) {
816
#print "Deleted plan record, with Pilot ID $_\n";
817
$planID{$_}->{'deleted'} = 1;
818
$planID{$_}->{'pilotid'} = $_;
819
$planID{$_}->{'id'} = $del;
820
$planRecord{$del} = $planID{$_};
825
msg( "Palm loop\n" ) if ($PREFS->{'Debug'} > 2);
827
foreach (keys %pilotID) {
828
$dlp->tickle unless (++$loop_count % 50);
830
# Palm records originally downloaded from a different Plan database
831
# are off-limits during this pass.
833
next if $dbname{$_} ne $control->{'name'};
836
msg( "Palm record: " . PrintPilotRecord($pilotID{$_}) . "\n" ) if ($PREFS->{'Debug'} > 1);
837
#print "Palm record: ",Dumper($pilotID{$_}),"\n";
838
if ($pilotID{$_}->{'deleted'} || $pilotID{$_}->{'archived'}) {
840
# # At this point are seeing Palm records marked as deleted or
841
# # archived. In the case of a slow sync, deleted records may not
842
# # be seen until a later pass.
844
# # Action: If there is an associated Plan record that has not
845
# # already been deleted, delete it.
847
# if (defined $planID{$_} and not $planID{$_}->{'deleted'}) {
848
# DeletePlanRecord($planID{$_});
849
# delete $planRecord{$planID{$_}->{'id'}};
850
# delete $planID{$_};
853
# # Remove the Pilot ID from the exception cache, if present
854
# delete $exceptID{$_};
856
# delete $lastID{$_};
858
# delete $pilothash{$_};
860
my ($hash) = HashPilotRecord($pilotID{$_});
862
######################################################
863
# If the pilot record ID is not cached, then it is
864
# definitely new. If the MD5 hash of the record is
865
# different from the cached hash, then it is
866
# definitely different. These checks are only needed
867
# during a slow sync (which will have inaccurate
868
# flags), but are harmless during a fast sync.
869
######################################################
871
#print "Old hash: $pilothash{$_}, new hash: $hash\n";
872
if ((not exists $pilothash{$_}) or ($hash ne $pilothash{$_})) {
873
$pilotID{$_}->{'modified'} = 1;
874
#print "Note: cache indicates record is changed\n";
876
$pilothash{$_} = $hash; # Record the hash and ID for the next sync
878
# Remove the record from the exception cache if it has been
879
# modified: perhaps it is not exceptional any more
881
delete $exceptID{$_} if $pilotID{$_}->{'modified'};
883
#print "Matching plan record: ", Dumper($planID{$_}),"\n";
885
if (not defined $planID{$_}) {
886
if (!$exceptID{$_}) {
887
# The Palm record has no matching Plan record
889
# Action: Install the Palm record in Plan, regardless of
892
msg( "Installing Palm record in Plan: ".
893
PrintPilotRecord($pilotID{$_}). "\n" ) if ($PREFS->{'Debug'});
895
#print "Installing pilot record in plan: ",Dumper($pilotID{$_});
897
my ($record) = RecordPilotToPlan($pilotID{$_});
898
if (not defined $record) {
899
# The record is not translatable to a Plan record.
901
# Action: Abort the install, and mark the record as
902
# uninstallable so that it will not be tried each sync.
903
# Code above will remove the exception flag when the
908
msg( "Palm record unsyncable\n" );
912
WritePlanRecord($socket, $record);
915
} elsif ($pilotID{$_}->{'modified'} and $planID{$_}->{'deleted'}) {
917
############################################
918
# The Palm record has a matching _deleted_
921
# This is collision, with a relatively
922
# simple solution. replace the Plan record
923
# with the Palm record. As the Plan record
924
# has already been permanently deleted, we
925
# need only copy the Palm record over.
927
# Action: Install the Palm record in Plan
928
############################################
931
my ($record) = RecordPilotToPlan($pilotID{$_}, $planID{$_});
932
if (not defined $record) {
933
# The record is not translatable to a Plan record.
935
# Action: Abort the install, and mark the record as
936
# uninstallable so that it will not be tried each sync.
940
msg( "Palm record modified while Plan record deleted, but new Palm record unsyncable\n" );
943
WritePlanRecord($socket, $record);
945
msg( "Palm record modified while Plan record deleted\n" ) if ($PREFS->{'Debug'} > 1);
948
} elsif ($pilotID{$_}->{'modified'} and $planID{$_}->{'modified'}) {
951
############################################
952
# The Palm record has a matching _modified_
955
# TODO: Use a comparator function to verify
956
# that the records are actually
957
# substantially different. If not, simply
960
# This is collision with an ugly, but
961
# lossless, solution. Neither the Palm or
962
# Plan record is inherantly preferable, so
963
# we duplicate each record on the other
964
# side, severing the link between the
965
# original new records, forging two new
966
# links and two new records, one on each
969
# Action: Install the Palm record in Plan as
970
# a new, distinct, record, and install the
971
# Plan record on the Palm as a new,
973
############################################
976
msg( "Conflicting modified Plan and Palm records\n" );
979
my ($record) = RecordPlanToPilot($planID{$_});
980
if (not defined $record) {
981
# The Plan record is not translatable to a Palm record.
983
# Action: Abort the install.
985
msg( "Conflicting Plan record unsyncable.\n" );
988
my ($id) = WritePilotRecord($db, $control, $record);
990
#$db->setRecord($record);
992
#my ($hash) = HashPilotRecord($record);
993
#$pilothash{$id} = $hash;
995
#$record->{'id'} = $id;
996
#$pilotID{$id} = $record;
997
#$dbname{$id} = $dbname;
999
$planID{$_}->{'pilotid'} = $id;
1001
$planID{$_}->{'modified'} = 0;
1003
WritePlanRecord($socket, $planID{$_});
1005
msg( "ID of new Palm record is $id\n" ) if ($PREFS->{'Debug'} > 2);
1010
my ($record) = RecordPilotToPlan($pilotID{$_});
1011
if (not defined $record) {
1012
# The Palm record is not translatable to a Plan record.
1014
# Action: Abort the install.
1018
msg( "Conflicting Palm record unsyncable.\n" );
1021
$record->{'modified'} = 0;
1023
my ($id) = WritePlanRecord($socket, $record);
1025
msg( "ID of new Plan record is $id\n" ) if ($PREFS->{'Debug'} > 2);
1029
} elsif($pilotID{$_}->{'modified'}) {
1031
##########################################
1032
# At this point, we have a changed Palm
1033
# record with an existing unmodified Plan
1036
# Action: Install the Palm record in Plan,
1037
# overwriting the Plan record.
1038
##########################################
1040
my ($record) = RecordPilotToPlan($pilotID{$_}, $planID{$_});
1041
if (not defined $record) {
1042
# The record is not translatable to a Plan record.
1044
# Action: Abort the install, and mark the record as
1045
# uninstallable so that it will not be tried each sync.
1046
# Code above will remove the exception flag when the
1047
# record is changed.
1050
DeletePlanRecord($socket, $planID{$_});
1052
msg( "Palm record modified while Plan record unchanged, but new Palm record unsyncable. Plan record has been deleted.\n" );
1055
#print "Overwriting plan record: ",Dumper($planID{$_});
1056
#print "With pilot record: ",Dumper($pilotID{$_});
1057
#print "As plan record: ",Dumper($record);
1059
WritePlanRecord($socket, $record);
1060
msg( "Updating Plan record with modified Palm record: ".PrintPilotRecord($pilotID{$_})."\n" ) if ($PREFS->{'Debug'});
1061
#print "New plan record state: ",Dumper($planID{$_}),"\n";
1067
msg( "Plan loop\n" ) if ($PREFS->{'Debug'} > 2);
1069
foreach (keys %planRecord) {
1070
$dlp->tickle unless (++$loop_count % 100);
1072
msg( "Plan record: " . PrintPlanRecord($planRecord{$_}),"\n" ) if ($PREFS->{'Debug'} > 1);
1073
my ($record) = $planRecord{$_};
1074
my ($pid) = $planRecord{$_}->{'pilotid'};
1076
#print "Plan record: ",Dumper($record),"\n";
1077
if ($record->{'deleted'}) {
1079
# # At this point are seeing Palm records marked as deleted or
1080
# # archived. In the case of a slow sync, deleted records may not
1081
# # be seen until a later pass.
1083
# # Action: If there is an associated Plan record that has not
1084
# # already been deleted, delete it.
1086
# if (defined $planID{$_} and not $planID{$_}->{'deleted'}) {
1087
# DeletePlanRecord($planID{$_});
1088
# delete $planRecord{$planID{$_}->{'id'}};
1089
# delete $planID{$_};
1092
# # Remove the Pilot ID from the exception cache, if present
1093
# delete $exceptID{$_};
1095
# delete $lastID{$_};
1097
# delete $pilothash{$_};
1100
# Remove the record from the exception cache if it has been
1101
# modified: perhaps it is not exceptional any more
1103
delete $record->{'pilotexcept'} if $record->{'modified'};
1105
# If this is a fast sync, it's possible the record hasn't been
1108
# This is dead code. Fast sync was never
1109
# implemented, so $slowsync is always 1. I'm
1110
# leaving it here as a hint in case someone
1111
# ever gets around to implementing fast sync.
1112
# But it looks incorrect to me:
1113
# LoadPilotRecord takes an index, not an
1116
if (!$slowsync and defined $pid and not exists $pilotID{$pid}) {
1117
my ($precord) = LoadPilotRecord($db, $pid);
1118
#$db->getRecord($pid);
1119
if (defined $precord) {
1120
if (not defined $dbname{$pid}) {
1121
$dbname{$pid} = $control->{'defaultname'};
1123
$pilotID{$pid} = $precord;
1127
if (defined $pid and defined $pilotID{$pid} and ($dbname{$pid} ne $control->{'name'})) {
1128
msg( "Weird: Plan database $control->{'name'} claims to own Palm record $pid,\n" );
1129
msg( "but my ID database says it is owned by $dbname{$pid}. I'll skip it.\n" );
1133
#print "Matching pilot record: ", Dumper($pilotID{$pid}),"\n";
1135
if (not defined $pid or not defined $pilotID{$pid}) {
1136
if (!$record->{'pilotexcept'}) {
1137
# The Plan record has no matching Palm record
1139
# Action: Install the Plan record in Palm, regardless of
1142
msg( "Installing Plan record in Palm: ".
1143
PrintPlanRecord($record). "\n" ) if ($PREFS->{'Debug'});
1145
#print "Installing plan record in pilot: ",Dumper($record);
1146
#print "Trying to install Plan record: ",Dumper($record),"\n";
1148
my ($newrecord) = RecordPlanToPilot($record);
1149
if (not defined $newrecord) {
1150
# The record is not translatable to a Palm record.
1152
# Action: Abort the install, and mark the record as
1153
# uninstallable so that it will not be tried each sync.
1154
# Code above will remove the exception flag when the
1155
# record is changed.
1157
$record->{'pilotexcept'} = 1;
1158
$record->{'modified'} = 1;
1160
msg( "Plan record unsyncable\n" );
1163
#print "Installing Palm record: ", Dumper($newrecord),"\n";
1165
$newrecord->{'id'} = 0;
1166
$newrecord->{'secret'} = 0;
1167
my ($id) = WritePilotRecord($db,$control,$newrecord);
1168
#$db->setRecord($newrecord);
1170
msg( "ID of new Palm record is $id\n" ) if ($PREFS->{'Debug'} > 2);
1172
#my ($hash) = HashPilotRecord($newrecord);
1173
#$pilothash{$id} = $hash;
1175
#$newrecord->{'id'} = $id;
1176
#$pilotID{$id} = $newrecord;
1177
#$dbname{$id} = $dbname;
1179
$record->{'pilotid'} = $id; # Match the Palm record to the Plan record
1180
$record->{'modified'} = 1; # and make sure it is written back out
1183
} elsif ($record->{'modified'} and $pilotID{$pid}->{'deleted'}) {
1185
# The Plan record has a matching _deleted_ Palm record.
1187
# This is collision, with a relatively simple solution.
1188
# replace the Palm record with the Plan record.
1190
# Action: Install the Plan record in Palm
1192
my ($newrecord) = RecordPlanToPilot($record, $pilotID{$pid});
1193
if (not defined $newrecord) {
1194
# The record is not translatable to a Palm record.
1196
# Action: Abort the install, and mark the record as
1197
# uninstallable so that it will not be tried each sync.
1199
$record->{'pilotexcept'} = 1;
1201
msg( "Plan record modified while Palm record deleted, but new Plan record unsyncable\n" );
1204
#print "Installing Palm record: ", Dumper($newrecord),"\n";
1205
WritePilotRecord($db,$control,$newrecord);
1206
#$db->setRecord($newrecord);
1207
#my ($hash) = HashPilotRecord($newrecord);
1208
#$pilothash{$pid} = $hash;
1210
msg( "Plan record modified while Palm record deleted\n" ) if ($PREFS->{'Debug'} > 1);
1213
} elsif ($record->{'modified'} and $pilotID{$pid}->{'modified'}) {
1214
croak("This shouldn't happen...");
1215
} elsif ($record->{'modified'}) {
1217
# At this point, we have a changed Plan record with an
1218
# existing unmodified Palm record.
1220
# Action: Install the Plan record in the Palm, overwriting the
1223
#print "Trying to install Plan record: ",Dumper($record),"\n";
1224
my ($newrecord) = RecordPlanToPilot($record, $pilotID{$pid});
1225
if (not defined $newrecord) {
1226
# The record is not translatable to a Plan record.
1228
# Action: Abort the install, and mark the record as
1229
# uninstallable so that it will not be tried each sync.
1230
# Code above will remove the exception flag when the
1231
# record is changed.
1233
$record->{'pilotexcept'} = 1;
1235
DeletePilotRecord($db,$pid);
1236
#$db->deleteRecord($record->{'pilotid'});
1237
#delete $pilothash{$record->{'pilotid'}};
1238
#delete $exceptID{$record->{'pilotid'}};
1240
msg( "Plan record modified while Palm record unchanged, but new Plan record unsyncable. Palm record has been deleted.\n" );
1243
#print "Overwriting pilot record: ",Dumper($pilotID{$_});
1244
#print "With plan record: ",Dumper($record);
1245
#print "As pilot record: ",Dumper($newrecord);
1247
#print "Installing Palm record: ", Dumper($newrecord),"\n";
1248
WritePilotRecord($db,$control,$newrecord);
1249
#$db->setRecord($newrecord);
1250
#my ($hash) = HashPilotRecord($newrecord);
1251
#$pilothash{$pid} = $hash;
1253
msg( "Updating Palm record with modified Plan record: ".PrintPlanRecord($record)."\n" ) if ($PREFS->{'Debug'});
1257
if ($record->{'modified'}) {
1258
WritePlanRecord($socket, $record);
1262
msg( "Palm delete loop\n" ) if ($PREFS->{'Debug'} > 2);
1264
foreach (keys %pilotID) {
1265
$dlp->tickle unless (++$loop_count % 100);
1267
############################################################
1268
# Palm records originally downloaded from a different Plan
1269
# database are off-limits during this pass.
1270
############################################################
1271
next if $dbname{$_} ne $control->{'name'};
1273
#print "Palm record: ",Dumper($pilotID{$_}),"\n";
1274
msg( "Palm record: " . PrintPilotRecord($pilotID{$_}) . "\n" ) if ($PREFS->{'Debug'} > 1);
1275
if ($pilotID{$_}->{'deleted'} || $pilotID{$_}->{'archived'}) {
1277
# At this point are seeing Palm records marked as deleted or
1278
# archived. In the case of a slow sync, deleted records may not
1279
# be seen until a later pass.
1281
# Action: If there is an associated Plan record that has not
1282
# already been deleted, delete it.
1284
msg( "Deleting Palm record.\n" ) if ($PREFS->{'Debug'} > 1);
1286
if (defined $planID{$_} and not $planID{$_}->{'deleted'}) {
1287
msg( "... and associated Plan record.\n" ) if ($PREFS->{'Debug'} > 1);
1288
msg( "Deleting from Plan: ". PrintPlanRecord($planRecord{$planID{$_}->{'id'}}) ."\n") if ($PREFS->{'Debug'});
1289
DeletePlanRecord($socket, $planID{$_});
1290
delete $planRecord{$planID{$_}->{'id'}};
1294
# Remove the Pilot ID from the exception cache, if present
1295
delete $exceptID{$_};
1297
delete $pilotID{$_};
1301
delete $pilothash{$_};
1305
msg( "Plan delete loop\n" ) if ($PREFS->{'Debug'} > 2);
1307
foreach (keys %planRecord) {
1308
$dlp->tickle unless (++$loop_count % 100);
1310
my ($record) = $planRecord{$_};
1311
my ($pid) = $planRecord{$_}->{'pilotid'};
1312
#print "Plan record: ",Dumper($record),"\n";
1313
msg( "Plan record: " . PrintPlanRecord($planRecord{$_}) . "\n" ) if ($PREFS->{'Debug'} > 1);
1315
# In a fast sync, we might not have loaded the record yet.
1317
# This is dead code. Fast sync was never implemented,
1318
# so $slowsync is always 1. I'm leaving it here as a
1319
# hint in case someone ever gets around to
1320
# implementing fast sync. But it looks incorrect to
1321
# me: LoadPilotRecord takes an index, not an id. -ANK
1323
if (!$slowsync and defined $pid and not exists $pilotID{$pid}) {
1324
my ($precord) = LoadPilotRecord($db, $pid);
1325
#$db->getRecord($pid);
1326
if (defined $precord) {
1327
if (not defined $dbname{$pid}) {
1328
$dbname{$pid} = $control->{'defaultname'};
1330
$pilotID{$pid} = $precord;
1334
if (defined $pid and defined $pilotID{$pid} and ($dbname{$pid} ne $control->{'name'})) {
1335
msg( "Weird: Plan database $control->{'name'} claims to own Palm record $pid,\n" );
1336
msg( "but my ID database says it is owned by $dbname{$pid}. I'll skip it.\n" );
1340
if ($record->{'deleted'}) {
1342
# At this point are seeing Palm records marked as deleted or
1343
# archived. In the case of a slow sync, deleted records may not
1344
# be seen until a later pass.
1346
# Action: If there is an associated Plan record that has not
1347
# already been deleted, delete it.
1349
msg( "Deleting Plan record.\n" ) if ($PREFS->{'Debug'} > 1);
1350
if (defined $pid and defined $pilotID{$pid} and not $pilotID{$_}->{'deleted'}) {
1351
msg( "... and associated Palm record.\n" ) if ($PREFS->{'Debug'} > 1);
1352
msg( "Deleting from Palm: " . PrintPilotRecord($pilotID{$pid}) ."\n" ) if ($PREFS->{'Debug'});
1353
DeletePilotRecord($db, $pid);
1354
#$db->deleteRecord($pid);
1355
#delete $pilotID{$pid};
1356
#delete $pilothash{$pid};
1357
#delete $exceptID{$pid};
1360
# Remove the Pilot ID from the exception cache, if present
1362
delete $planRecord{$_};
1369
############################################################
1371
############################################################
1372
sub loadpilotrecords {
1373
msg( "Loading pilot records:\n" );
1375
if ($dlp->getStatus<0) {
1376
croak "Cancelled.\n";
1379
msg( "Synchronizing pilot called '$pilotname'\n" ) if ($PREFS->{'Debug'} > 1);
1381
if (not defined $control{$pilotname}) {
1382
msg( "Database access list for Palm has not been defined!\n\n" );
1383
msg( "Palm '$pilotname' has been added to $controldir/control.\n" );
1384
msg( "Please edit $controldir/control and add the names of the Plan databases\n" );
1385
msg( "that this Palm should synchronize with.\n" );
1387
open (C, ">>$controldir/control");
1388
print C "$pilotname\n";
1393
$db = $dlp->open("DatebookDB");
1397
my $max = $db->getRecords();
1399
status("Reading Palm Appointments", 0);
1400
while(defined($r = LoadPilotRecord($db,$i++))) {
1401
status("Reading Palm Appointments", int(100*$i/$max))
1402
if ($i % (int($max/20)+1) == 0);
1404
status("Reading Palm Appointments", 100);
1405
msg( "Done reading records\n" ) if ($PREFS->{'Debug'} > 1);
1410
foreach (keys %pilothash) {
1411
if (not exists $pilotID{$_}) {
1412
$pilotID{$_}->{'deleted'} = 1;
1419
############################################################
1421
############################################################
1422
sub SendPlanCommand {
1423
my ($socket,$text) = @_;
1425
#print "Sending |$text|\n";
1426
while (length($text)) {
1427
$len = syswrite $socket, $text, length($text);
1428
$text = substr($text,$len);
1432
my ($partialReply) = "";
1434
############################################################
1436
############################################################
1443
while ($partialReply =~ /\A(.*?)(\\)?\n/m) {
1446
if (not defined($2)) {
1447
$reply =~ s/\\\n/\n/sg;
1448
$reply =~ s/\n$//sg;
1450
if ($reply =~ /\AR/) { # Discard
1452
} elsif ($reply =~ /\A\?/) { # Discard
1453
msg( "Plan message: $'" );
1456
#print "Reply: |$reply|\n";
1463
sysread($socket,$buf,1024);
1464
$partialReply .= $buf;
1465
} while ($buf !~ /[^\\]\n|\A\n/);
1466
# ^^ the regexp matches if $buf contains an unescaped
1467
# newline, i.e. a newline that's either the first
1468
# character, or preceded by a non-escape character.
1473
############################################################
1475
############################################################
1477
my ($db, $control) = @_;
1479
my $dbname = $control->{'dbname'};
1481
#print "Opening database $control->{'name'}\@$control->{'host'}:$control->{'port'}.\n";
1483
my $socket = IO::Socket::INET->new(PeerPort => $control->{'port'}, PeerAddr => $control->{'host'}, Proto => 'tcp');
1485
if (not defined $socket) {
1486
croak "Unable to open plan socket on $control->{'host'}:$control->{'port'}\n";
1489
$socket->autoflush(1);
1491
my $select = IO::Select->new();
1493
$select->add($socket);
1495
my $reply=ReadPlanReply($socket);
1497
if ($reply !~ /^!/) {
1498
croak "Unknown response from netplan: $reply\n";
1501
$netplanversion = $reply;
1504
SendPlanCommand($socket, "=sync-plan<uid=$<,gid=$>,pid=$$>\n");
1506
SendPlanCommand($socket, "o$dbname\n");
1507
$reply = ReadPlanReply($socket);
1509
if ($reply !~ /^otw(\d+)/) {
1510
croak "Failed to open database $control->{'name'}\@$control->{'host'}:$control->{'port'}.\n";
1514
SendPlanCommand($socket, "n$file\n");
1515
$reply = ReadPlanReply($socket);
1517
if ($reply !~ /^n\d+\s+(\d+)/) {
1518
croak "Failed to get record count.\n";
1525
SendPlanCommand($socket, "r$file 0\n");
1527
$reply = ReadPlanReply($socket);
1528
if ($reply =~ /\Art\d+\s+(\d+)\s+/) {
1530
#print "Got ID $1\n";
1535
my ($loop_count) = (0);
1537
$dlp->tickle unless (++$loop_count % 50);
1538
SendPlanCommand($socket, "l$file $_\n");
1539
$reply = ReadPlanReply($socket);
1541
if ($reply !~ /^lt/) {
1542
croak "Failed to lock record $_.\n";
1545
SendPlanCommand($socket, "r$file $_\n");
1546
$reply = ReadPlanReply($socket);
1548
if ($reply !~ /\Art\d+\s+(\d+)\s+/s) {
1549
croak "Didn't get record I was looking for.\n";
1552
dorecord($db, $socket, $control, $_, $');
1555
doafterplan($db, $socket, $control);
1557
%planRecord = (); # Flush plan records
1559
SendPlanCommand($socket, "c$file\n");
1564
############################################################
1566
############################################################
1569
if (! -d $controldir) {
1570
croak "Directory $controldir does not exist. It must be created before $0 is run.\n\n";
1573
if (! -f "$controldir/control") {
1574
open(C, ">$controldir/control") || croak "Unable to write to $controldir/control";
1575
print C "# this file is used to control which Palms are allowed to sync, and what databases\n";
1576
print C "# each Palm will sync with. Each line consists of whitespace-separated fields, the\n";
1577
print C "# first one being the name (and ID) of the Palm, and subsequent fields listing\n";
1578
print C "# all plan databases that Palm will synchronize with.\n";
1580
print C "# For example: Foo_s_Pilot_1234 myname\@localhost group\@host.io ro:all\@localhostn";
1582
print C "# New entries on the Palm are installed in the first database listed.\n";
1583
print C "# Records will not exchanged between separate plan datatabses.\n";
1584
print C "# A database may be prefixed with 'rw:' or 'ro:' to indicate read/write (the\n";
1585
print C "# default) or read only access. If a database is read-only, any record changes\n";
1586
print C "# on the Palm will be discarded. However, for technical reasons, you must have\n";
1587
print C "# read/write access to the plan database itself.\n";
1591
open(C,"<$controldir/control");
1595
my ($i,@i) = split(/\s+/, $_);
1600
my ($mode, $name, $host) = m/^(?:(wr|ro|rw):)?([^\@]+)(?:\@(.+))?$/;
1601
if (not defined $mode) {
1604
if (not defined $host) {
1605
$host = "localhost";
1607
if ($mode !~ /^rw$/) {
1608
croak "Access mode $mode (for Palm '$i') at line $. of $controldir/control unknown or unsupported.\n";
1611
$defaultname = $name.'@'.$host;
1613
push @I, {mode => $mode, name => $name.'@'.$host, dbname => $name, host => $host, port => $PREFS->{'NetplanPort'}, 'read' => ($mode =~ /r/), 'write' => ($mode =~ /w/), default => $first, defaultname => $defaultname};
1616
$control{$i} = [@I];
1621
############################################################
1623
############################################################
1629
# initialize variables that may still be set from last sync (which
1630
# can happen when conduitSync is called from PilotManager).
1639
$pilotname = $db = $slowsync = $file = $maxseed = $netplanversion = undef;
1643
$pilotname = $info->{'name'} . "_ " . $info->{'userID'};
1644
$pilotname =~ s/[^A-Za-z0-9]+/_/g;
1646
foreach (@{$control{$pilotname}}) {
1647
$sawName{$_->{'name'}} = 1;
1650
if (open (I, "<$controldir/ids.$pilotname")) {
1653
my ($id, $hash, $except, $dbname) = split(/\s+/, $_);
1654
$pilothash{$id} = $hash;
1655
$exceptID{$id} = $except;
1656
if (not defined $dbname or not length $dbname) {
1657
$dbname = $control{$pilotname}->[0]->{'name'};
1659
$dbname{$id} = $dbname if defined $dbname and length $dbname;
1660
#print Dumper({dbname=>$dbname{$id}});
1661
if (not defined $sawName{$dbname}) {
1662
msg( "Warning! The ID file, $controldir/ids.$pilotname, lists a record as belonging\n" );
1663
msg( "to database $dbname, but the control file $controldir/control does not list this\n" );
1664
msg( "this database. If you have renamed a database, please edit $controldir/ids.$pilotname\n" );
1665
msg( "so all references to this database match the new name.\n" );
1666
msg( "\nIf you wish to delete all on the Palm that were originally from $dbname, then\n" );
1667
msg( "delete the database name from the end of each record's line.\n" );
1668
msg( "To merge the records into the default database, delete each affected line entirely.\n" );
1670
$sawName{$dbname} = 1;
1678
if (loadpilotrecords) {
1680
if (!@{$control{$pilotname}}) {
1681
msg( "No plan databases are registered for the '$pilotname' Palm. Please\n" );
1682
msg( "edit $controldir/control and add one or more databases.\n" );
1685
foreach (keys %pilotID) {
1686
if (not defined $dbname{$_}) {
1687
$dbname{$_} = $control{$pilotname}->[0]->{'name'};
1691
foreach (@{$control{$pilotname}}) {
1692
next if not defined $_->{'host'}; # Sigh. Autoviv problem.
1696
# Delete deleted & archived records
1699
# Clear modified flags, and set last sync time to now
1704
open (I, ">$controldir/ids.$pilotname");
1705
foreach (keys %pilothash) {
1706
if ($dbname{$_} eq $control{$pilotname}->[0]{'name'}) {
1709
$exceptID{$_} = 0 unless (defined $exceptID{$_});
1710
print I "$_ $pilothash{$_} $exceptID{$_} $dbname{$_}\n";
1718
############################################################
1720
############################################################
1722
my ($tempdlp, $tempinfo);
1725
croak "Usage: $0 <pilot port> <control directory>\n\n<control directory> is where various information is stored.\nYou might wish to use " .
1726
(getpwuid($>))[7] . "/.sync-plan\n";
1730
$controldir = $ARGV[1];
1732
$controldir =~ s/\/+$//;
1734
msg "Please start HotSync now.\n";
1735
my $psocket = PDA::Pilot::openPort($port);
1738
croak "Unable to open port $port\n";
1740
($tempdlp = PDA::Pilot::accept($psocket)) || croak "Can't connect to Palm";
1742
($tempinfo = $tempdlp->getUserInfo) || croak "Lost connection to Palm";
1744
conduitSync(undef, $tempdlp, $tempinfo);
1747
PDA::Pilot::close($psocket);