~ubuntu-branches/ubuntu/karmic/pilot-link/karmic

« back to all changes in this revision

Viewing changes to src/sync-plan.PL

  • Committer: Bazaar Package Importer
  • Author(s): Ludovic Rousseau
  • Date: 2007-02-14 23:30:59 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20070214233059-znxrekb2mgjr0mgd
Tags: 0.12.2-1
* New upstream release
 - Closes: #410152 "pilot-link: udev rules need updating"
* debian/control: add Build-Depends: libbluetooth2-dev
 - add bluetooth support

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
use Config;
2
 
print $Config{startperl}, "\n";
3
 
print <DATA>;
4
 
__DATA__;
5
 
 
6
 
use IO::Socket;
7
 
use IO::Select;
8
 
use Time::Local;
9
 
use Digest::MD5;
10
 
use PDA::Pilot;
11
 
use Carp;
12
 
use strict;
13
 
 
14
 
my ($controldir, $dlp, $info, $db, $port);
15
 
my (%control, %pilothash, %pilotID, %planID, %exceptID, %planRecord,
16
 
    %dbname, %sawName);
17
 
my ($slowsync, $file, $pilotname, $maxseed, $netplanversion);
18
 
 
19
 
my $PREFS = {
20
 
             NetplanPort => 5444,
21
 
             Debug       => 1,
22
 
            };
23
 
 
24
 
my @plversion;          # pilot-link version (version, major, minor, patch)
25
 
 
26
 
# any or alll of these may be undefined, depending on the
27
 
# pilot-link version.
28
 
eval {
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();
33
 
};
34
 
 
35
 
# msg and status are here to localize the differences between the
36
 
# standalone sync-plan.PL and the SyncPlan.pm module for PilotManager.
37
 
 
38
 
############################################################
39
 
#
40
 
############################################################
41
 
sub msg {
42
 
  print @_;
43
 
}
44
 
 
45
 
sub status {
46
 
}
47
 
 
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
 
############################################################
55
 
BEGIN {
56
 
  package ErrorCheck;
57
 
  use Carp;
58
 
  sub checkErrNotFound
59
 
    {
60
 
      my($obj) = @_;
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()) {
64
 
          croak "Error $errno";
65
 
        }
66
 
        if (($errno = $obj->palmos_errno()) != PDA::Pilot::dlpErrNotFound()) {
67
 
          croak "Error $errno: " . PDA::Pilot::errorText($errno);
68
 
        }
69
 
      } else {
70
 
        croak "Error $errno" if ($errno != -5); # dlpErrNotFound
71
 
      }
72
 
    }
73
 
}
74
 
*checkErrNotFound = \&ErrorCheck::checkErrNotFound;
75
 
 
76
 
 
77
 
############################################################
78
 
#
79
 
############################################################
80
 
sub DatePlanToPerl {
81
 
        my ($PlanDate)  = @_;
82
 
        my ($m,$d,$y)   = split(m!/!,$PlanDate);
83
 
        if ($y < 40) {
84
 
                $y += 100;
85
 
        }
86
 
        if ($y > 1900) {
87
 
                $y -= 1900;
88
 
        }
89
 
        $m--;
90
 
 
91
 
        timegm(0,0,0,$d,$m,$y);
92
 
}
93
 
 
94
 
############################################################
95
 
#
96
 
############################################################
97
 
sub TimePlanToPerl {
98
 
        my ($PlanTime)  = @_;
99
 
        my ($h,$m,$s)   = split(m!:!,$PlanTime);
100
 
        
101
 
        return undef if $h == 99 and $m == 99 and $s == 99;
102
 
        
103
 
        $s + ($m * 60) + ($h * 60 * 60);
104
 
}
105
 
 
106
 
############################################################
107
 
#
108
 
############################################################
109
 
sub TimePerlToPlan {
110
 
        my ($PerlDT) = @_;
111
 
        return "99:99:99" if not defined $PerlDT;
112
 
 
113
 
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
114
 
            gmtime($PerlDT);
115
 
        
116
 
        "$hour:$min:$sec";
117
 
}
118
 
 
119
 
############################################################
120
 
#
121
 
############################################################
122
 
sub TimeRelPerlToPlan {
123
 
        my ($PerlDT) = @_;
124
 
        return "99:99:99" if not defined $PerlDT;
125
 
 
126
 
        my ($sec,$min,$hour);
127
 
        
128
 
        $hour = int($PerlDT/ (60*60));
129
 
        $PerlDT -= $hour*60*60;
130
 
 
131
 
        $min = int($PerlDT/ (60));
132
 
        $PerlDT -= $min*60;
133
 
 
134
 
        $sec = int($PerlDT);
135
 
        $PerlDT -= $sec;
136
 
        
137
 
        "$hour:$min:$sec";
138
 
}
139
 
 
140
 
############################################################
141
 
#
142
 
############################################################
143
 
sub DatePilotToPerl {
144
 
        my ($s,$m,$h, $mday,$mon,$year) = @_;
145
 
 
146
 
        if (ref $s eq 'ARRAY') {
147
 
            ($s,$m,$h, $mday,$mon,$year) = @$s;
148
 
        }
149
 
        my ($date, $time);
150
 
 
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")
154
 
              if $@;
155
 
            $time = $s + 60 * ($m + 60 * $h);
156
 
        }
157
 
        else {
158
 
            msg("Bad year: $year");
159
 
        }
160
 
 
161
 
        return wantarray ? ($date, $time) : $date;
162
 
}
163
 
 
164
 
############################################################
165
 
#
166
 
############################################################
167
 
sub DatePerlToPlan {
168
 
        my ($PerlDT) = @_;
169
 
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
170
 
            gmtime($PerlDT);
171
 
        
172
 
        $year += 1900;
173
 
        $mon++;
174
 
        
175
 
        "$mon/$mday/$year";
176
 
}
177
 
 
178
 
############################################################
179
 
#
180
 
############################################################
181
 
sub RecordPlanToPilot {
182
 
        my ($plan,$pilot) = @_;
183
 
        if (not defined $pilot) {
184
 
                $pilot = PDA::Pilot::AppointmentDatabase->record;
185
 
        }
186
 
        
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'} ||= "";
191
 
 
192
 
        if (defined $plan->{'time'}) {
193
 
                $pilot->{'begin'} = [gmtime($plan->{'date'}+$plan->{'time'})];
194
 
                $pilot->{'end'} = [gmtime($plan->{'date'}+$plan->{'time'}+$plan->{'length'})];
195
 
                $pilot->{'event'}=0;
196
 
        } else {
197
 
                $pilot->{'begin'} = [gmtime($plan->{'date'})];
198
 
                $pilot->{'event'}       = 1;
199
 
                $plan->{'early'}        = 0;
200
 
                $plan->{'late'}         = 0;
201
 
        }
202
 
        
203
 
        if ($plan->{'early'} and $plan->{'late'} and ($plan->{'early'} != $plan->{'late'})) {
204
 
                msg( "Two alarms - using earlier one." );
205
 
                $plan->{'late'} = $plan->{'early'};
206
 
        }
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));
215
 
                } else {
216
 
                        $pilot->{'alarm'}->{'units'} = "minutes";
217
 
                        $pilot->{'alarm'}->{'advance'} = int($alarm / 60);
218
 
                }
219
 
        }
220
 
        
221
 
        if (defined $plan->{'exceptions'}) {
222
 
                foreach (@{$plan->{'exceptions'}}) {
223
 
                        push @{$pilot->{'exceptions'}}, [gmtime($_)];
224
 
                }
225
 
        } else {
226
 
                delete $pilot->{'exceptions'};
227
 
        }
228
 
 
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])];
234
 
                }
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";
246
 
                        
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));
251
 
                        }
252
 
                        # If the weekday list does include the day the event is one, abort
253
 
                        if (!$pilot->{'repeat'}{'days'}[$pilot->{'begin'}[6]]) {
254
 
                                return undef;
255
 
                        }
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)) {
261
 
                                return undef;
262
 
                        }
263
 
                        if (($weekday & 4096) and ($weekday & 8192)) {
264
 
                                $weekday &= ~4096;
265
 
                        }
266
 
                        if ($week == 4) {
267
 
                                $week = 5;
268
 
                        }
269
 
                        if (($weekday & 0xff00) != (256<<$week)) {
270
 
                                return undef;
271
 
                        }
272
 
                        if ($week == 5) {
273
 
                                $week = 4;
274
 
                        }
275
 
                        
276
 
                        $pilot->{'repeat'}->{'type'} = "MonthlyByDay";
277
 
                        $pilot->{'repeat'}->{'day'} = $week*7+$wday;
278
 
                } else {
279
 
                        return undef;
280
 
                }
281
 
        } else {
282
 
                delete $pilot->{'repeat'};
283
 
        }
284
 
        
285
 
        $pilot;
286
 
}
287
 
 
288
 
############################################################
289
 
#
290
 
############################################################
291
 
sub RecordPilotToPlan {
292
 
        my ($pilot,$plan) = @_;
293
 
        $plan = {color => 0} if not defined $plan;
294
 
        
295
 
        $plan->{'pilotid'} = $pilot->{'id'};
296
 
        $plan->{'id'} ||= 0;
297
 
        $plan->{'message'} = [split("\xA", $pilot->{'note'})] if defined $pilot->{'note'};
298
 
        $plan->{'note'} = [split("\xA", $pilot->{'description'})] if defined $pilot->{'description'};
299
 
 
300
 
        my ($date, $time) = DatePilotToPerl($pilot->{'begin'});
301
 
        unless ($date) {
302
 
                msg("Begin time in Palm record untranslatable.");
303
 
                return undef;
304
 
        }
305
 
 
306
 
        $plan->{'date'} = $date;
307
 
        if ($pilot->{'event'}) {
308
 
                $plan->{'time'} = undef;
309
 
                $plan->{'length'} = 0;
310
 
        } else {
311
 
                $plan->{'time'} = $time;
312
 
                my $end = DatePilotToPerl($pilot->{'end'});
313
 
                unless ($end) {
314
 
                    msg("End time in Palm record untranslatable.");
315
 
                    return undef;
316
 
                }
317
 
                $plan->{'length'} = $end - $date;
318
 
        }
319
 
        
320
 
        if (exists $pilot->{'alarm'}) {
321
 
                my($alarm) = 0;
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);
328
 
                }
329
 
                if ($plan->{'late'}) {
330
 
                        $plan->{'late'} = $alarm;
331
 
                        $plan->{'early'} = 0;
332
 
                } else {
333
 
                        $plan->{'late'} = 0;
334
 
                        $plan->{'early'} = $alarm;
335
 
                }
336
 
        } else {
337
 
                $plan->{'late'}=0;
338
 
                $plan->{'early'}=0;
339
 
        }
340
 
        
341
 
        if (exists $pilot->{'exceptions'}) {
342
 
                # Plan records can only deal with four exceptions, 
343
 
                if (@{$pilot->{'exceptions'}} > 4) {
344
 
                        msg("Too many exceptions.");
345
 
                        return undef;
346
 
                }
347
 
                foreach (@{$pilot->{'exceptions'}}) {
348
 
                        push @{$plan->{'exceptions'}}, timegm(@{$_});
349
 
                }
350
 
        }
351
 
 
352
 
        delete $plan->{'repeat'};
353
 
        
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;
361
 
                
362
 
                } elsif ($pilot->{'repeat'}->{'type'} eq "Weekly" and ($pilot->{'repeat'}->{'frequency'}==1)) {
363
 
                        my ($r) = 0;
364
 
                        foreach my $i (0..6) {
365
 
                                if ($pilot->{'repeat'}->{'days'}[$i]) {
366
 
                                        $r |= (1<<$i);
367
 
                                }
368
 
                        }
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.
373
 
                        my $count = 0;
374
 
                        foreach my $i (0..6) {
375
 
                                $count ++ if ($pilot->{repeat}->{days}[$i]);
376
 
                        }
377
 
                        if ($count == 1) {
378
 
                                $plan->{'repeat'}->[0] = (60*60*24) * $pilot->{'repeat'}->{'frequency'} * 7;
379
 
                                $plan->{'repeat'}->[4] = 0;
380
 
                        } else {
381
 
                                msg("Repeat pattern too complex.");
382
 
                                return undef;
383
 
                        }
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);
391
 
                } else {
392
 
                        msg("Repeat pattern too complex.");
393
 
                        return undef;
394
 
                }
395
 
                if (defined $pilot->{'repeat'}->{'end'}) {
396
 
                        $plan->{'repeat'}->[1] = timegm(@{$pilot->{'repeat'}->{'end'}});
397
 
                }
398
 
        }
399
 
        
400
 
        $plan;
401
 
}
402
 
 
403
 
############################################################
404
 
#
405
 
############################################################
406
 
sub generaterecord {
407
 
        my ($rec) = @_;
408
 
        my (@output);
409
 
        
410
 
        #print "Generating Plan record: ", Dumper($rec),"\n";
411
 
 
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" : "-").
425
 
                                "-".
426
 
                                "-".
427
 
                                " ".$rec->{'color'});
428
 
 
429
 
        if (defined $rec->{'repeat'}) {
430
 
                push @output, "R\t".join(" ",@{$rec->{'repeat'}});
431
 
        }
432
 
        if (defined $rec->{'exceptions'}) {
433
 
                foreach (@{$rec->{'exceptions'}}) {
434
 
                        push @output, "E\t".DatePerlToPlan($_);
435
 
                }
436
 
        }
437
 
        if (defined $rec->{'note'}) {
438
 
                push @output, map("N\t$_", @{$rec->{'note'}});
439
 
        }
440
 
        if (defined $rec->{'message'}) {
441
 
                push @output, map("M\t$_", @{$rec->{'message'}});
442
 
        }
443
 
        if (defined $rec->{'script'}) {
444
 
                push @output, map("S\t$_", @{$rec->{'script'}});
445
 
        }
446
 
        if (defined $rec->{'other'}) {
447
 
                foreach (@{$rec->{'other'}}) {
448
 
                        push @output, $_;
449
 
                }
450
 
        }
451
 
 
452
 
        my ($hash) = new Digest::MD5;
453
 
        foreach (@output) {
454
 
                #print "Adding |$_| to hash\n";
455
 
                $hash->add($_);
456
 
        }
457
 
        $rec->{'pilothash'} = $hash->hexdigest;
458
 
        {
459
 
                my ($i);
460
 
                for ($i=0;$i<@output;$i++) {
461
 
                        last if $output[$i] =~ /^S/;
462
 
                }
463
 
                $rec->{'pilotexcept'} += 0;
464
 
                my (@US);
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;
468
 
        }
469
 
        
470
 
        msg( "Generated record |" . join("\n", @output). "|\n" ) if ($PREFS->{'Debug'} > 2);
471
 
 
472
 
        join("\n",@output);
473
 
}
474
 
 
475
 
############################################################
476
 
#
477
 
############################################################
478
 
sub PrintPlanRecord {
479
 
        my ($rec) = @_;
480
 
        my ($output);
481
 
        
482
 
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
483
 
            gmtime($rec->{'date'});
484
 
        $year += 1900;
485
 
        $mon++;
486
 
        $output = "$year/$mon/$mday";
487
 
 
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);
492
 
 
493
 
                ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
494
 
                  gmtime($rec->{'time'}+$rec->{'length'});
495
 
                $output .= sprintf("%02d:%02d", $hour, $min);
496
 
        }
497
 
        $output .= " '".join("\\n",@{$rec->{'note'}})."'" if defined $rec->{'note'};
498
 
        $output .= " (".join("\\n",@{$rec->{'message'}}).")" if defined $rec->{'message'};
499
 
        
500
 
        if ($rec->{'repeat'}) {
501
 
                my (@r);
502
 
                if ($rec->{'repeat'}[0]) {
503
 
                        push @r, "every " . ($rec->{'repeat'}[0] / (60*60*24)) . " days";
504
 
                }
505
 
                
506
 
                if ($rec->{'repeat'}[4]) {
507
 
                        push @r, "every year";
508
 
                }
509
 
                if ($rec->{'repeat'}[3]) {
510
 
                        my ($i) = $rec->{'repeat'}[3];
511
 
                        if ($i & 1) {
512
 
                                push @r, "the last day of each month";
513
 
                        }
514
 
                        foreach (1..31) {
515
 
                                push @r, "the $_ of each month" if $i & (1<<$_);
516
 
                        }
517
 
                }
518
 
                if ($rec->{'repeat'}[2]) {
519
 
                        push @r, "until ".scalar(gmtime($rec->{'repeat'}[2]));
520
 
                }
521
 
                if (@r) {
522
 
                        $output .= " repeat ".join(", ", @r);
523
 
                }
524
 
        }
525
 
        
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'});
532
 
#       $output .= "}";
533
 
        
534
 
        $output;
535
 
}
536
 
 
537
 
############################################################
538
 
#
539
 
############################################################
540
 
sub PrintPilotRecord {
541
 
        my ($rec) = @_;
542
 
        my ($output);
543
 
        
544
 
        $output = ($rec->{'begin'}[5]+1900)."/".($rec->{'begin'}[4]+1)."/".$rec->{'begin'}[3];
545
 
        
546
 
        if (!$rec->{'event'}) {
547
 
                $output .= " ";
548
 
                $output .= sprintf("%02d:%02d-%02d:%02d",
549
 
                                   $rec->{'begin'}[2],
550
 
                                   $rec->{'begin'}[1],
551
 
                                   $rec->{'end'}[2],
552
 
                                   $rec->{'end'}[1]);
553
 
        }
554
 
        
555
 
        $output .= " '$rec->{'description'}'";
556
 
        $output .= " ($rec->{'message'})" if (defined $rec->{'message'});
557
 
        
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'});
564
 
#       $output .= "}";
565
 
 
566
 
        $output =~ s/\r/\\r/g;
567
 
        $output =~ s/\n/\\n/g;
568
 
        
569
 
        $output;
570
 
}
571
 
 
572
 
############################################################
573
 
#
574
 
# Takes a Plan record in hash format
575
 
576
 
############################################################
577
 
sub WritePlanRecord {
578
 
        my ($socket, $record) = @_; 
579
 
        my ($raw) = generaterecord($record);
580
 
        my ($reply);
581
 
        $record->{'id'} ||= 0;
582
 
        #print "ID is $record->{'id'}\n";
583
 
        $raw =~ s/\n/\\\n/g;
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";
596
 
        } else {
597
 
                msg( "Failed write: $reply\n" );
598
 
        }       
599
 
}
600
 
 
601
 
 
602
 
############################################################
603
 
#
604
 
############################################################
605
 
sub LoadPilotRecord {
606
 
        my ($db, $i) = @_;
607
 
        my ($record) = $db->getRecord($i);
608
 
        if ($record) {
609
 
                $pilotID{$record->{'id'}} = $record;
610
 
        } else {
611
 
                checkErrNotFound($db);
612
 
        }
613
 
        $record;
614
 
}
615
 
 
616
 
############################################################
617
 
#
618
 
# takes a Plan record in hash format
619
 
#
620
 
############################################################
621
 
sub DeletePlanRecord {
622
 
        my ($socket, $record) = @_; 
623
 
        my ($raw);
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);
628
 
}
629
 
 
630
 
############################################################
631
 
#
632
 
# takes a Palm record in hash format
633
 
#
634
 
############################################################
635
 
sub WritePilotRecord {
636
 
        my ($db, $control, $record) = @_; 
637
 
        
638
 
        $record->{'id'} ||= 0;
639
 
        $record->{'category'} ||= 0;
640
 
        
641
 
        #print "Installing record in Palm: ",Dumper($record);
642
 
        
643
 
        my ($id) = $db->setRecord($record);
644
 
        
645
 
        if ($id) {
646
 
                $pilotID{$id}   = $record;
647
 
                my ($hash)      = HashPilotRecord($record);                                             
648
 
                $pilothash{$id} = $hash;
649
 
                $dbname{$id}    = $control->{'name'};
650
 
                $record->{'id'} = $id;
651
 
                $exceptID{$id}  = 0;
652
 
        }
653
 
        
654
 
        $id;
655
 
}
656
 
 
657
 
############################################################
658
 
#
659
 
############################################################
660
 
sub DeletePilotRecord {
661
 
        my ($db, $id) = @_; 
662
 
        my ($result) = $db->deleteRecord($id);
663
 
        if ($result>=0) {
664
 
                delete $pilothash{$id};
665
 
                delete $pilotID{$id};
666
 
                delete $dbname{$id};
667
 
                delete $exceptID{$id};
668
 
        }
669
 
        $result;
670
 
}
671
 
 
672
 
 
673
 
$maxseed = 0;
674
 
 
675
 
############################################################
676
 
#
677
 
############################################################
678
 
sub dorecord {
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;
685
 
        $l[0] =~ s/\s+/ /g;
686
 
        $hash->add($l[0]);
687
 
        my ($date, $time, $length, $early, $late, $flags, $color) = split(/\s+/, shift @l);
688
 
        $rec->{'pilotrec'} = "";
689
 
        foreach (@l) {
690
 
                if (/^E\t/) {
691
 
                        push @E, $';
692
 
                } elsif (/^M\t/) {
693
 
                        push @M, $';
694
 
                } elsif (/^N\t/) {
695
 
                        push @N, $';
696
 
                } elsif (/^S\t/) {
697
 
                        my ($s) = $';
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;
706
 
                                                $planID{$id} = $rec;
707
 
                                                next; 
708
 
                                        }
709
 
                                }
710
 
                                push @US, $_;
711
 
                                next; # skip hash add
712
 
                        } else {
713
 
                                push @S, $s;
714
 
                        }
715
 
                } elsif (/^R\t/) {
716
 
                        my ($r) = $';
717
 
                        $r =~ s/\s+/ /g;
718
 
                        $rec->{'repeat'} = [split(/\s+/, $r)];
719
 
                } else {
720
 
                        push @{$rec->{'other'}}, $_;
721
 
                }
722
 
                #print "Adding |$_| to hash\n";
723
 
                $hash->add($_);
724
 
        }
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;
739
 
 
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;
749
 
        $rec->{'id'}            = $i;
750
 
        
751
 
        $rec->{'exceptions'} = [map(DatePlanToPerl($_), @E)] if @E;
752
 
        
753
 
        $planRecord{$i} = $rec;
754
 
        
755
 
        #print "Read plan record:\n";
756
 
        #print Dumper($rec);
757
 
}
758
 
 
759
 
############################################################
760
 
#
761
 
############################################################
762
 
sub HashPilotRecord {
763
 
        my ($record) = @_;
764
 
        my ($hash) = new Digest::MD5;
765
 
        $hash->add($record->{'raw'});
766
 
        $hash->hexdigest;
767
 
}
768
 
 
769
 
 
770
 
############################################################
771
 
#
772
 
############################################################
773
 
sub doafterplan {
774
 
        my ($db,$socket,$control) = @_;
775
 
        msg( "After stuff:\n" ) if ($PREFS->{'Debug'} > 2);
776
 
 
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
782
 
        # unmodified record.
783
 
        ##################################################################
784
 
        
785
 
        my (@uniq) = sort {$a->{'pilotid'} <=> $b->{'pilotid'} or $a->{'modified'} <=> $b->{'modified'}} grep {exists $_->{'pilotid'}} values %planRecord;
786
 
        my ($i) = 0;
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;
794
 
                }
795
 
        }
796
 
 
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);
804
 
 
805
 
        my ($del) = -1;
806
 
        foreach (keys %pilothash) {
807
 
 
808
 
                # Palm records originally downloaded from a different Plan database
809
 
                # are off-limits during this pass.
810
 
                
811
 
                next if $dbname{$_} ne $control->{'name'}; 
812
 
                
813
 
 
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{$_};
821
 
                        $del--;
822
 
                }
823
 
        }
824
 
 
825
 
        msg( "Palm loop\n" ) if ($PREFS->{'Debug'} > 2);
826
 
 
827
 
        foreach (keys %pilotID) {
828
 
                $dlp->tickle unless (++$loop_count % 50);
829
 
 
830
 
                # Palm records originally downloaded from a different Plan database
831
 
                # are off-limits during this pass.
832
 
                
833
 
                next if $dbname{$_} ne $control->{'name'}; 
834
 
                
835
 
                
836
 
                msg( "Palm record: " . PrintPilotRecord($pilotID{$_}) . "\n" ) if ($PREFS->{'Debug'} > 1);
837
 
                #print "Palm record: ",Dumper($pilotID{$_}),"\n";
838
 
                if ($pilotID{$_}->{'deleted'} || $pilotID{$_}->{'archived'}) {
839
 
                #       
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.
843
 
                #       
844
 
                #       # Action: If there is an associated Plan record that has not
845
 
                #       # already been deleted, delete it.
846
 
                #       
847
 
                #       if (defined $planID{$_} and not $planID{$_}->{'deleted'}) {
848
 
                #               DeletePlanRecord($planID{$_});
849
 
                #               delete $planRecord{$planID{$_}->{'id'}};
850
 
                #               delete $planID{$_};
851
 
                #       }
852
 
                #
853
 
                #       # Remove the Pilot ID from the exception cache, if present
854
 
                #       delete $exceptID{$_};
855
 
                #       
856
 
                #       delete $lastID{$_};
857
 
                #
858
 
                #       delete $pilothash{$_};
859
 
                } else {
860
 
                        my ($hash) = HashPilotRecord($pilotID{$_});
861
 
                        
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
 
                        ######################################################
870
 
                        
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";
875
 
                        }
876
 
                        $pilothash{$_} = $hash; # Record the hash and ID for the next sync
877
 
                        
878
 
                        # Remove the record from the exception cache if it has been
879
 
                        # modified: perhaps it is not exceptional any more
880
 
 
881
 
                        delete $exceptID{$_} if $pilotID{$_}->{'modified'};
882
 
                        
883
 
                        #print "Matching plan record: ", Dumper($planID{$_}),"\n";
884
 
                        
885
 
                        if (not defined $planID{$_}) {
886
 
                                if (!$exceptID{$_}) {
887
 
                                        # The Palm record has no matching Plan record
888
 
                                        
889
 
                                        # Action: Install the Palm record in Plan, regardless of
890
 
                                        # changed status
891
 
                                        
892
 
                                        msg( "Installing Palm record in Plan: ".
893
 
                                                PrintPilotRecord($pilotID{$_}). "\n" ) if ($PREFS->{'Debug'});
894
 
                                        
895
 
                                        #print "Installing pilot record in plan: ",Dumper($pilotID{$_});
896
 
                                        
897
 
                                        my ($record) = RecordPilotToPlan($pilotID{$_});
898
 
                                        if (not defined $record) {
899
 
                                                # The record is not translatable to a Plan record. 
900
 
                                                
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
904
 
                                                # record is changed.
905
 
                                                
906
 
                                                $exceptID{$_} = 1;
907
 
        
908
 
                                                msg( "Palm record unsyncable\n" );
909
 
        
910
 
                                        } else {
911
 
                                        
912
 
                                                WritePlanRecord($socket, $record);
913
 
                                        }
914
 
                                }
915
 
                        } elsif ($pilotID{$_}->{'modified'} and $planID{$_}->{'deleted'}) {
916
 
 
917
 
                                ############################################
918
 
                                # The Palm record has a matching _deleted_
919
 
                                # Plan record.
920
 
                                
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.
926
 
                                
927
 
                                # Action: Install the Palm record in Plan
928
 
                                ############################################
929
 
 
930
 
                                                                
931
 
                                my ($record) = RecordPilotToPlan($pilotID{$_}, $planID{$_});
932
 
                                if (not defined $record) {
933
 
                                        # The record is not translatable to a Plan record. 
934
 
                                        
935
 
                                        # Action: Abort the install, and mark the record as
936
 
                                        # uninstallable so that it will not be tried each sync.
937
 
                                        
938
 
                                        $exceptID{$_} = 1;
939
 
                                        
940
 
                                        msg( "Palm record modified while Plan record deleted, but new Palm record unsyncable\n" );
941
 
                                } else {
942
 
 
943
 
                                        WritePlanRecord($socket, $record);
944
 
 
945
 
                                        msg( "Palm record modified while Plan record deleted\n" ) if ($PREFS->{'Debug'} > 1);
946
 
                                }
947
 
                                
948
 
                        } elsif ($pilotID{$_}->{'modified'} and $planID{$_}->{'modified'}) {
949
 
 
950
 
 
951
 
                                ############################################
952
 
                                # The Palm record has a matching _modified_
953
 
                                # Plan record.
954
 
                                
955
 
                                # TODO: Use a comparator function to verify
956
 
                                # that the records are actually
957
 
                                # substantially different. If not, simply
958
 
                                # skip any action.
959
 
                                
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
967
 
                                # side.
968
 
                                
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,
972
 
                                # distinct, record.
973
 
                                ############################################
974
 
 
975
 
                                
976
 
                                msg( "Conflicting modified Plan and Palm records\n" );
977
 
                                
978
 
                                {
979
 
                                        my ($record) = RecordPlanToPilot($planID{$_});
980
 
                                        if (not defined $record) {
981
 
                                                # The Plan record is not translatable to a Palm record. 
982
 
                                                
983
 
                                                # Action: Abort the install.
984
 
        
985
 
                                                msg( "Conflicting Plan record unsyncable.\n" );
986
 
                                        } else {
987
 
                                                $record->{'id'} = 0;
988
 
                                                my ($id) = WritePilotRecord($db, $control, $record);
989
 
                                                
990
 
                                                #$db->setRecord($record);
991
 
                                                #
992
 
                                                #my ($hash) = HashPilotRecord($record);                                         
993
 
                                                #$pilothash{$id} = $hash;
994
 
                                                #
995
 
                                                #$record->{'id'} = $id;
996
 
                                                #$pilotID{$id} = $record;
997
 
                                                #$dbname{$id} = $dbname;
998
 
                                                
999
 
                                                $planID{$_}->{'pilotid'} = $id;
1000
 
                                                
1001
 
                                                $planID{$_}->{'modified'} = 0;
1002
 
                        
1003
 
                                                WritePlanRecord($socket, $planID{$_});
1004
 
                                                
1005
 
                                                msg( "ID of new Palm record is $id\n" ) if ($PREFS->{'Debug'} > 2);
1006
 
                                        }
1007
 
                                }
1008
 
                                
1009
 
                                {
1010
 
                                        my ($record) = RecordPilotToPlan($pilotID{$_});
1011
 
                                        if (not defined $record) {
1012
 
                                                # The Palm record is not translatable to a Plan record. 
1013
 
                                                
1014
 
                                                # Action: Abort the install.
1015
 
        
1016
 
                                                $exceptID{$_} = 1;
1017
 
        
1018
 
                                                msg( "Conflicting Palm record unsyncable.\n" );
1019
 
                                        } else {
1020
 
                                        
1021
 
                                                $record->{'modified'} = 0;
1022
 
                                                
1023
 
                                                my ($id) = WritePlanRecord($socket, $record);
1024
 
 
1025
 
                                                msg( "ID of new Plan record is $id\n" ) if ($PREFS->{'Debug'} > 2);
1026
 
 
1027
 
                                        }
1028
 
                                }
1029
 
                        } elsif($pilotID{$_}->{'modified'}) {
1030
 
                        
1031
 
                                ##########################################
1032
 
                                # At this point, we have a changed Palm
1033
 
                                # record with an existing unmodified Plan
1034
 
                                # record.
1035
 
                                
1036
 
                                # Action: Install the Palm record in Plan,
1037
 
                                # overwriting the Plan record.
1038
 
                                ##########################################
1039
 
                                                                
1040
 
                                my ($record) = RecordPilotToPlan($pilotID{$_}, $planID{$_});
1041
 
                                if (not defined $record) {
1042
 
                                        # The record is not translatable to a Plan record. 
1043
 
                                        
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.
1048
 
                                        
1049
 
                                        $exceptID{$_} = 1;
1050
 
                                        DeletePlanRecord($socket, $planID{$_});
1051
 
                                        
1052
 
                                        msg( "Palm record modified while Plan record unchanged, but new Palm record unsyncable. Plan record has been deleted.\n" );
1053
 
                                } else {
1054
 
                                
1055
 
                                        #print "Overwriting plan record: ",Dumper($planID{$_});
1056
 
                                        #print "With pilot record: ",Dumper($pilotID{$_});
1057
 
                                        #print "As plan record: ",Dumper($record);
1058
 
                                
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";
1062
 
                                }
1063
 
                        }
1064
 
                }
1065
 
        }
1066
 
        $dlp->tickle;
1067
 
        msg( "Plan loop\n" ) if ($PREFS->{'Debug'} > 2);
1068
 
 
1069
 
        foreach (keys %planRecord) {
1070
 
                $dlp->tickle unless (++$loop_count % 100);
1071
 
 
1072
 
                msg( "Plan record: " . PrintPlanRecord($planRecord{$_}),"\n" ) if ($PREFS->{'Debug'} > 1);
1073
 
                my ($record) = $planRecord{$_};
1074
 
                my ($pid) = $planRecord{$_}->{'pilotid'};
1075
 
                
1076
 
                #print "Plan record: ",Dumper($record),"\n";
1077
 
                if ($record->{'deleted'}) {
1078
 
                #       
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.
1082
 
                #       
1083
 
                #       # Action: If there is an associated Plan record that has not
1084
 
                #       # already been deleted, delete it.
1085
 
                #       
1086
 
                #       if (defined $planID{$_} and not $planID{$_}->{'deleted'}) {
1087
 
                #               DeletePlanRecord($planID{$_});
1088
 
                #               delete $planRecord{$planID{$_}->{'id'}};
1089
 
                #               delete $planID{$_};
1090
 
                #       }
1091
 
                #
1092
 
                #       # Remove the Pilot ID from the exception cache, if present
1093
 
                #       delete $exceptID{$_};
1094
 
                #       
1095
 
                #       delete $lastID{$_};
1096
 
                #
1097
 
                #       delete $pilothash{$_};
1098
 
                } else {
1099
 
 
1100
 
                        # Remove the record from the exception cache if it has been
1101
 
                        # modified: perhaps it is not exceptional any more
1102
 
 
1103
 
                        delete $record->{'pilotexcept'}  if $record->{'modified'};
1104
 
                        
1105
 
                        # If this is a fast sync, it's possible the record hasn't been
1106
 
                        # fetched yet.
1107
 
 
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
1114
 
                        # id. -ANK
1115
 
 
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'};
1122
 
                                        }
1123
 
                                        $pilotID{$pid} = $precord;
1124
 
                                }
1125
 
                        }
1126
 
                        
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" );
1130
 
                                next;
1131
 
                        }
1132
 
                        
1133
 
                        #print "Matching pilot record: ", Dumper($pilotID{$pid}),"\n";
1134
 
                        
1135
 
                        if (not defined $pid or not defined $pilotID{$pid}) {
1136
 
                                if (!$record->{'pilotexcept'}) {
1137
 
                                        # The Plan record has no matching Palm record
1138
 
                                        
1139
 
                                        # Action: Install the Plan record in Palm, regardless of
1140
 
                                        # changed status
1141
 
                                        
1142
 
                                        msg( "Installing Plan record in Palm: ".
1143
 
                                                PrintPlanRecord($record). "\n" ) if ($PREFS->{'Debug'});
1144
 
 
1145
 
                                        #print "Installing plan record in pilot: ",Dumper($record);
1146
 
                                        #print "Trying to install Plan record: ",Dumper($record),"\n";
1147
 
                                        
1148
 
                                        my ($newrecord) = RecordPlanToPilot($record);
1149
 
                                        if (not defined $newrecord) {
1150
 
                                                # The record is not translatable to a Palm record. 
1151
 
                                                
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.
1156
 
                                                
1157
 
                                                $record->{'pilotexcept'} = 1;
1158
 
                                                $record->{'modified'} = 1;
1159
 
                                                
1160
 
                                                msg( "Plan record unsyncable\n" );
1161
 
        
1162
 
                                        } else {
1163
 
                                                #print "Installing Palm record: ", Dumper($newrecord),"\n";
1164
 
                                                
1165
 
                                                $newrecord->{'id'} = 0;
1166
 
                                                $newrecord->{'secret'} = 0;
1167
 
                                                my ($id) = WritePilotRecord($db,$control,$newrecord);
1168
 
                                                #$db->setRecord($newrecord);
1169
 
 
1170
 
                                                msg( "ID of new Palm record is $id\n" ) if ($PREFS->{'Debug'} > 2);
1171
 
                                                
1172
 
                                                #my ($hash) = HashPilotRecord($newrecord);                                              
1173
 
                                                #$pilothash{$id} = $hash;
1174
 
                                                #
1175
 
                                                #$newrecord->{'id'} = $id;
1176
 
                                                #$pilotID{$id} = $newrecord;
1177
 
                                                #$dbname{$id} = $dbname;
1178
 
                                                
1179
 
                                                $record->{'pilotid'} = $id; # Match the Palm record to the Plan record
1180
 
                                                $record->{'modified'} = 1;  # and make sure it is written back out
1181
 
                                        }
1182
 
                                }
1183
 
                        } elsif ($record->{'modified'} and $pilotID{$pid}->{'deleted'}) {
1184
 
 
1185
 
                                # The Plan record has a matching _deleted_ Palm record.
1186
 
                                
1187
 
                                # This is collision, with a relatively simple solution.
1188
 
                                # replace the Palm record with the Plan record. 
1189
 
                                
1190
 
                                # Action: Install the Plan record in Palm
1191
 
                                                                
1192
 
                                my ($newrecord) = RecordPlanToPilot($record, $pilotID{$pid});
1193
 
                                if (not defined $newrecord) {
1194
 
                                        # The record is not translatable to a Palm record. 
1195
 
                                        
1196
 
                                        # Action: Abort the install, and mark the record as
1197
 
                                        # uninstallable so that it will not be tried each sync.
1198
 
                                        
1199
 
                                        $record->{'pilotexcept'} = 1;
1200
 
                                        
1201
 
                                        msg( "Plan record modified while Palm record deleted, but new Plan record unsyncable\n" );
1202
 
                                } else {
1203
 
 
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;
1209
 
 
1210
 
                                        msg( "Plan record modified while Palm record deleted\n" ) if ($PREFS->{'Debug'} > 1);
1211
 
                                }
1212
 
                                
1213
 
                        } elsif ($record->{'modified'} and $pilotID{$pid}->{'modified'}) {
1214
 
                                croak("This shouldn't happen...");
1215
 
                        } elsif ($record->{'modified'}) {
1216
 
                        
1217
 
                                # At this point, we have a changed Plan record with an
1218
 
                                # existing unmodified Palm record.
1219
 
                                
1220
 
                                # Action: Install the Plan record in the Palm, overwriting the
1221
 
                                # Palm record.
1222
 
                                
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. 
1227
 
                                        
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.
1232
 
                                        
1233
 
                                        $record->{'pilotexcept'} = 1;
1234
 
                                        
1235
 
                                        DeletePilotRecord($db,$pid);
1236
 
                                        #$db->deleteRecord($record->{'pilotid'});
1237
 
                                        #delete $pilothash{$record->{'pilotid'}};
1238
 
                                        #delete $exceptID{$record->{'pilotid'}};
1239
 
                                        
1240
 
                                        msg( "Plan record modified while Palm record unchanged, but new Plan record unsyncable. Palm record has been deleted.\n" );
1241
 
                                } else {
1242
 
 
1243
 
                                        #print "Overwriting pilot record: ",Dumper($pilotID{$_});
1244
 
                                        #print "With plan record: ",Dumper($record);
1245
 
                                        #print "As pilot record: ",Dumper($newrecord);
1246
 
 
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;
1252
 
                                        
1253
 
                                        msg( "Updating Palm record with modified Plan record: ".PrintPlanRecord($record)."\n" ) if ($PREFS->{'Debug'});
1254
 
                                }
1255
 
                        }
1256
 
                }
1257
 
                if ($record->{'modified'}) {
1258
 
                        WritePlanRecord($socket, $record);
1259
 
                }
1260
 
        }
1261
 
 
1262
 
        msg( "Palm delete loop\n" ) if ($PREFS->{'Debug'} > 2);
1263
 
 
1264
 
        foreach (keys %pilotID) {
1265
 
                $dlp->tickle unless (++$loop_count % 100);
1266
 
 
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'}; 
1272
 
 
1273
 
                #print "Palm record: ",Dumper($pilotID{$_}),"\n";
1274
 
                msg( "Palm record: " . PrintPilotRecord($pilotID{$_}) . "\n" ) if ($PREFS->{'Debug'} > 1);
1275
 
                if ($pilotID{$_}->{'deleted'} || $pilotID{$_}->{'archived'}) {
1276
 
                        
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.
1280
 
                        
1281
 
                        # Action: If there is an associated Plan record that has not
1282
 
                        # already been deleted, delete it.
1283
 
                        
1284
 
                        msg( "Deleting Palm record.\n" ) if ($PREFS->{'Debug'} > 1);
1285
 
                        
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'}};
1291
 
                                delete $planID{$_};
1292
 
                        }
1293
 
                
1294
 
                        # Remove the Pilot ID from the exception cache, if present
1295
 
                        delete $exceptID{$_};
1296
 
                        
1297
 
                        delete $pilotID{$_};
1298
 
                        
1299
 
                        delete $dbname{$_};
1300
 
                
1301
 
                        delete $pilothash{$_};
1302
 
                }
1303
 
        }
1304
 
        
1305
 
        msg( "Plan delete loop\n" ) if ($PREFS->{'Debug'} > 2);
1306
 
 
1307
 
        foreach (keys %planRecord) {
1308
 
                $dlp->tickle unless (++$loop_count % 100);
1309
 
        
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);
1314
 
        
1315
 
                # In a fast sync, we might not have loaded the record yet.
1316
 
                
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
1322
 
 
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'};
1329
 
                                }
1330
 
                                $pilotID{$pid} = $precord;
1331
 
                        }
1332
 
                }
1333
 
                
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" );
1337
 
                        next;
1338
 
                }
1339
 
                
1340
 
                if ($record->{'deleted'}) {
1341
 
                        
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.
1345
 
                        
1346
 
                        # Action: If there is an associated Plan record that has not
1347
 
                        # already been deleted, delete it.
1348
 
                        
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};
1358
 
                        }
1359
 
                
1360
 
                        # Remove the Pilot ID from the exception cache, if present
1361
 
                        
1362
 
                        delete $planRecord{$_};
1363
 
                }
1364
 
        }
1365
 
        
1366
 
 
1367
 
}
1368
 
 
1369
 
############################################################
1370
 
#
1371
 
############################################################
1372
 
sub loadpilotrecords {
1373
 
        msg( "Loading pilot records:\n" );
1374
 
 
1375
 
        if ($dlp->getStatus<0) {
1376
 
                croak "Cancelled.\n";
1377
 
        }
1378
 
        
1379
 
        msg( "Synchronizing pilot called '$pilotname'\n" ) if ($PREFS->{'Debug'} > 1);
1380
 
        
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" );
1386
 
                
1387
 
                open (C, ">>$controldir/control");
1388
 
                print C "$pilotname\n";
1389
 
                close (C);
1390
 
                return 0;
1391
 
        }
1392
 
        
1393
 
        $db = $dlp->open("DatebookDB");
1394
 
 
1395
 
        my ($r, $i);
1396
 
        $i=0;
1397
 
        my $max = $db->getRecords();
1398
 
        $max ||= 1;
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);
1403
 
        }
1404
 
        status("Reading Palm Appointments", 100);
1405
 
        msg( "Done reading records\n" ) if ($PREFS->{'Debug'} > 1);
1406
 
 
1407
 
        $slowsync = 1;
1408
 
 
1409
 
        if ($slowsync) {
1410
 
                foreach (keys %pilothash) {
1411
 
                        if (not exists $pilotID{$_}) {
1412
 
                                $pilotID{$_}->{'deleted'} = 1;
1413
 
                        }
1414
 
                }
1415
 
        }
1416
 
        return 1;
1417
 
}
1418
 
 
1419
 
############################################################
1420
 
#
1421
 
############################################################
1422
 
sub SendPlanCommand {
1423
 
        my ($socket,$text) = @_;
1424
 
        my ($len);
1425
 
        #print "Sending |$text|\n";
1426
 
        while (length($text)) {
1427
 
                $len = syswrite $socket, $text, length($text);
1428
 
                $text = substr($text,$len);
1429
 
        }
1430
 
}
1431
 
 
1432
 
my ($partialReply) = "";
1433
 
 
1434
 
############################################################
1435
 
#
1436
 
############################################################
1437
 
sub ReadPlanReply {
1438
 
        my ($socket) = @_;
1439
 
        my ($reply) = "";
1440
 
        my ($buf);
1441
 
 
1442
 
        while (1) {
1443
 
                while ($partialReply =~ /\A(.*?)(\\)?\n/m) {
1444
 
                        $reply .= $1."\n";
1445
 
                        $partialReply = $';
1446
 
                        if (not defined($2)) {
1447
 
                                $reply =~ s/\\\n/\n/sg;
1448
 
                                $reply =~ s/\n$//sg;
1449
 
                                
1450
 
                                if ($reply =~ /\AR/) {  # Discard 
1451
 
                                        next;
1452
 
                                } elsif ($reply =~ /\A\?/) {    # Discard
1453
 
                                        msg( "Plan message: $'" );
1454
 
                                        next;
1455
 
                                } else {
1456
 
                                        #print "Reply: |$reply|\n";
1457
 
                                        return $reply;
1458
 
                                }
1459
 
                                $reply = "";
1460
 
                        }
1461
 
                }
1462
 
                do {
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.
1469
 
        }
1470
 
}
1471
 
        
1472
 
 
1473
 
############################################################
1474
 
#
1475
 
############################################################
1476
 
sub SyncDB {
1477
 
        my ($db, $control) = @_;
1478
 
 
1479
 
        my $dbname = $control->{'dbname'};
1480
 
        
1481
 
        #print "Opening database $control->{'name'}\@$control->{'host'}:$control->{'port'}.\n";
1482
 
 
1483
 
        my $socket = IO::Socket::INET->new(PeerPort => $control->{'port'}, PeerAddr => $control->{'host'}, Proto => 'tcp');
1484
 
 
1485
 
        if (not defined $socket) {
1486
 
                croak "Unable to open plan socket on $control->{'host'}:$control->{'port'}\n";
1487
 
        }
1488
 
 
1489
 
        $socket->autoflush(1);
1490
 
 
1491
 
        my $select = IO::Select->new();
1492
 
   
1493
 
        $select->add($socket);
1494
 
 
1495
 
        my $reply=ReadPlanReply($socket);
1496
 
 
1497
 
        if ($reply !~ /^!/) {   
1498
 
                croak "Unknown response from netplan: $reply\n";
1499
 
        }
1500
 
 
1501
 
        $netplanversion = $reply;
1502
 
 
1503
 
        # Authenticate
1504
 
        SendPlanCommand($socket, "=sync-plan<uid=$<,gid=$>,pid=$$>\n");
1505
 
 
1506
 
        SendPlanCommand($socket, "o$dbname\n");
1507
 
        $reply = ReadPlanReply($socket);
1508
 
        
1509
 
        if ($reply !~ /^otw(\d+)/) {
1510
 
                croak "Failed to open database $control->{'name'}\@$control->{'host'}:$control->{'port'}.\n";
1511
 
        }
1512
 
        $file = $1;
1513
 
        
1514
 
        SendPlanCommand($socket, "n$file\n");
1515
 
        $reply = ReadPlanReply($socket);
1516
 
        
1517
 
        if ($reply !~ /^n\d+\s+(\d+)/) {
1518
 
                croak "Failed to get record count.\n";
1519
 
        }
1520
 
        my $records = $1;
1521
 
 
1522
 
 
1523
 
        my @id= ();
1524
 
                
1525
 
        SendPlanCommand($socket, "r$file 0\n");
1526
 
        while ($records) {
1527
 
                $reply = ReadPlanReply($socket);
1528
 
                if ($reply =~ /\Art\d+\s+(\d+)\s+/) {
1529
 
                        push @id, $1;
1530
 
                        #print "Got ID $1\n";
1531
 
                        $records--;
1532
 
                }
1533
 
        }
1534
 
 
1535
 
        my ($loop_count) = (0);
1536
 
        foreach (@id) {
1537
 
                $dlp->tickle unless (++$loop_count % 50);
1538
 
                SendPlanCommand($socket, "l$file $_\n");
1539
 
                $reply = ReadPlanReply($socket);
1540
 
                
1541
 
                if ($reply !~ /^lt/) {
1542
 
                        croak "Failed to lock record $_.\n";
1543
 
                }
1544
 
        
1545
 
                SendPlanCommand($socket, "r$file $_\n");
1546
 
                $reply = ReadPlanReply($socket);
1547
 
                
1548
 
                if ($reply !~ /\Art\d+\s+(\d+)\s+/s) {
1549
 
                        croak "Didn't get record I was looking for.\n";
1550
 
                }
1551
 
                
1552
 
                dorecord($db, $socket, $control, $_, $');
1553
 
        }
1554
 
        
1555
 
        doafterplan($db, $socket, $control);
1556
 
 
1557
 
        %planRecord = ();  # Flush plan records
1558
 
 
1559
 
        SendPlanCommand($socket, "c$file\n");
1560
 
 
1561
 
        $socket->close;
1562
 
}
1563
 
 
1564
 
############################################################
1565
 
#
1566
 
############################################################
1567
 
sub readControlfile
1568
 
{
1569
 
    if (! -d $controldir) {
1570
 
        croak "Directory $controldir does not exist. It must be created before $0 is run.\n\n";
1571
 
    }
1572
 
 
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";
1579
 
        print C "#\n";
1580
 
        print C "# For example: Foo_s_Pilot_1234 myname\@localhost group\@host.io ro:all\@localhostn";
1581
 
        print C "#\n";
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";
1588
 
        close(C);
1589
 
    }
1590
 
 
1591
 
    open(C,"<$controldir/control");
1592
 
    while (<C>) {
1593
 
        chomp;
1594
 
        next if /^#/;
1595
 
        my ($i,@i) = split(/\s+/, $_);
1596
 
        my (@I);
1597
 
        my ($first) = 1;
1598
 
        my ($defaultname);
1599
 
        foreach (@i) {
1600
 
            my ($mode, $name, $host) = m/^(?:(wr|ro|rw):)?([^\@]+)(?:\@(.+))?$/;
1601
 
            if (not defined $mode) {
1602
 
                $mode = "rw";
1603
 
            }
1604
 
            if (not defined $host) {
1605
 
                $host = "localhost";
1606
 
            }
1607
 
            if ($mode !~ /^rw$/) {
1608
 
                croak "Access mode $mode (for Palm '$i') at line $. of $controldir/control unknown or unsupported.\n";
1609
 
            }
1610
 
            if ($first) {
1611
 
                $defaultname = $name.'@'.$host;
1612
 
            }
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};
1614
 
            $first = 0;
1615
 
        }
1616
 
        $control{$i} = [@I];
1617
 
    }
1618
 
    close(C);
1619
 
}
1620
 
 
1621
 
############################################################
1622
 
#
1623
 
############################################################
1624
 
sub conduitSync
1625
 
{
1626
 
    $dlp = $_[1];
1627
 
    $info = $_[2];
1628
 
 
1629
 
    # initialize variables that may still be set from last sync (which
1630
 
    # can happen when conduitSync is called from PilotManager).
1631
 
    %control = ();
1632
 
    %pilothash = ();
1633
 
    %pilotID = ();
1634
 
    %planID = ();
1635
 
    %exceptID = ();
1636
 
    %planRecord = ();
1637
 
    %dbname = ();
1638
 
    %sawName = ();
1639
 
    $pilotname = $db = $slowsync = $file = $maxseed = $netplanversion = undef;
1640
 
 
1641
 
    readControlfile;
1642
 
 
1643
 
    $pilotname = $info->{'name'} . "_ " . $info->{'userID'};
1644
 
    $pilotname =~ s/[^A-Za-z0-9]+/_/g;
1645
 
 
1646
 
    foreach (@{$control{$pilotname}}) {
1647
 
        $sawName{$_->{'name'}} = 1;
1648
 
    }
1649
 
 
1650
 
    if (open (I, "<$controldir/ids.$pilotname")) {
1651
 
        foreach (<I>) {
1652
 
            chop;
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'};
1658
 
            }
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" );
1669
 
                        
1670
 
                $sawName{$dbname} = 1;
1671
 
            }
1672
 
        }
1673
 
 
1674
 
        close (I);
1675
 
    }
1676
 
        
1677
 
 
1678
 
    if (loadpilotrecords) {
1679
 
 
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" );
1683
 
        }
1684
 
 
1685
 
        foreach (keys %pilotID) {
1686
 
            if (not defined $dbname{$_}) {
1687
 
                $dbname{$_} = $control{$pilotname}->[0]->{'name'};
1688
 
            }
1689
 
        }
1690
 
        
1691
 
        foreach (@{$control{$pilotname}}) {
1692
 
            next if not defined $_->{'host'}; # Sigh. Autoviv problem.
1693
 
            SyncDB($db, $_);
1694
 
        }
1695
 
 
1696
 
        # Delete deleted & archived records
1697
 
        $db->purge;
1698
 
        
1699
 
        # Clear modified flags, and set last sync time to now
1700
 
        $db->resetFlags;
1701
 
 
1702
 
        $db->close;
1703
 
 
1704
 
        open (I, ">$controldir/ids.$pilotname");
1705
 
        foreach (keys %pilothash) {
1706
 
            if ($dbname{$_} eq $control{$pilotname}->[0]{'name'}) {
1707
 
                $dbname{$_}="";
1708
 
            }
1709
 
            $exceptID{$_} = 0 unless (defined $exceptID{$_});
1710
 
            print I "$_ $pilothash{$_} $exceptID{$_} $dbname{$_}\n";
1711
 
        }
1712
 
        close(I);
1713
 
 
1714
 
 
1715
 
    }
1716
 
}
1717
 
 
1718
 
############################################################
1719
 
# main
1720
 
############################################################
1721
 
 
1722
 
my ($tempdlp, $tempinfo);
1723
 
 
1724
 
if (@ARGV<2) {
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";
1727
 
}
1728
 
 
1729
 
$port = $ARGV[0];
1730
 
$controldir = $ARGV[1];
1731
 
 
1732
 
$controldir =~ s/\/+$//;
1733
 
 
1734
 
msg "Please start HotSync now.\n";
1735
 
my $psocket = PDA::Pilot::openPort($port);
1736
 
 
1737
 
if (!$psocket) {
1738
 
    croak "Unable to open port $port\n";
1739
 
}
1740
 
($tempdlp = PDA::Pilot::accept($psocket)) || croak "Can't connect to Palm";
1741
 
 
1742
 
($tempinfo = $tempdlp->getUserInfo) || croak "Lost connection to Palm";
1743
 
 
1744
 
conduitSync(undef, $tempdlp, $tempinfo);
1745
 
 
1746
 
$dlp->close();
1747
 
PDA::Pilot::close($psocket);