]> git.pld-linux.org Git - packages/pilot-link.git/blame - pilot-link.sync-ldif.patch
- spec adapterized.
[packages/pilot-link.git] / pilot-link.sync-ldif.patch
CommitLineData
ab932e04 1diff -Nru pilot-link.0.9.3/Makefile.in pilot-link.0.9.3.new/Makefile.in
2--- pilot-link.0.9.3/Makefile.in Fri May 28 15:55:00 1999
3+++ pilot-link.0.9.3.new/Makefile.in Thu May 27 21:53:29 1999
4@@ -82,6 +82,7 @@
5 install-todos$(EXT) pilot-addresses$(EXT) pilot-clip$(EXT)\
6 read-ical$(EXT) pilot-mail$(EXT) read-expenses$(EXT) @ccexecs@ \
7 reminders$(EXT) memos$(EXT) addresses$(EXT) read-todos$(EXT)\
8+ sync-ldif$(EXT) \
9 debugsh$(EXT) dlpsh$(EXT) \
10 getrom$(EXT) pi-getrom$(EXT) pi-getram$(EXT) pi-port$(EXT) \
11 pi-csd$(EXT) pi-nredir$(EXT) \
12@@ -161,6 +162,9 @@
13 pilot-addresses$(EXT): $(PILIB) $(GETOPT) pilot-addresses.o
14 $(CCLINK) pilot-addresses.o $(PILIB) $(GETOPT) -o $@ $(LIBS)
15
16+sync-ldif$(EXT): $(PILIB) $(GETOPT) sync-ldif.o line64.o
17+ $(CCLINK) sync-ldif.o line64.o $(PILIB) $(GETOPT) -o $@ $(LIBS)
18+
19 pilot-dedupe$(EXT): $(PILIB) $(GETOPT) pilot-dedupe.o
20 $(CCLINK) pilot-dedupe.o $(PILIB) $(GETOPT) -o $@ $(LIBS)
21
22@@ -275,10 +279,10 @@
23
24 Tcl: FORCE
25 cd Tcl; $(MAKE)
26-
27+
28 Java: FORCE
29 cd Java; $(MAKE)
30-
31+
32 check test tests: FORCE
33 cd tests; $(MAKE) tests
34
35@@ -326,6 +330,12 @@
36 gcc -o tarball scripts/tarball.c -Iinclude
37 ./tarball
38
39+sync-ldif.usage.h: README.sync-ldif
40+ sed -n \
41+ -e '/_START_SYNC_LDIF_USAGE/,/_END_SYNC_LDIF_USAGE/ { s/^_.*//gp' \
42+ -e 's/^\(.*\)$$/"\1\\n"/gp' \
43+ -e '}' < README.sync-ldif > sync-ldif.usage.h
44+
45 #Depend information starts here. Do not edit the text beyond this point!
46 addresses.o: addresses.c include/pi-source.h include/pi-config.h \
47 include/pi-socket.h include/pi-args.h include/pi-version.h \
48@@ -356,6 +366,7 @@
49 include/pi-config.h include/pi-socket.h include/pi-args.h \
50 include/pi-version.h include/pi-sockaddr.h include/pi-macros.h \
51 include/pi-dlp.h include/pi-hinote.h include/pi-appinfo.h
52+line64.o: line64.c
53 install-memo.o: install-memo.c include/pi-source.h include/pi-config.h \
54 include/pi-socket.h include/pi-args.h include/pi-version.h \
55 include/pi-sockaddr.h include/pi-macros.h include/pi-dlp.h \
56@@ -451,3 +462,8 @@
57 include/pi-socket.h include/pi-args.h include/pi-version.h \
58 include/pi-sockaddr.h include/pi-macros.h include/pi-datebook.h \
59 include/pi-appinfo.h include/pi-dlp.h
60+sync-ldif.o: sync-ldif.c include/pi-source.h include/pi-config.h \
61+ include/pi-socket.h include/pi-args.h include/pi-version.h \
62+ include/pi-sockaddr.h include/pi-macros.h include/pi-dlp.h \
63+ include/pi-address.h include/pi-appinfo.h include/pi-sync.h \
64+ sync-ldif.usage.h
65diff -Nru pilot-link.0.9.3/Makefile.os2 pilot-link.0.9.3.new/Makefile.os2
66--- pilot-link.0.9.3/Makefile.os2 Fri May 14 08:55:37 1999
67+++ pilot-link.0.9.3.new/Makefile.os2 Thu May 27 21:52:46 1999
68@@ -82,6 +82,7 @@
69 install-todos$(EXT) pilot-addresses$(EXT) pilot-clip$(EXT)\
70 read-ical$(EXT) pilot-mail$(EXT) read-expenses$(EXT) $(CCEXECS) \
71 reminders$(EXT) memos$(EXT) addresses$(EXT) read-todos$(EXT)\
72+ sync-ldif$(EXT) \
73 debugsh$(EXT) dlpsh$(EXT) \
74 getrom$(EXT) pi-getrom$(EXT) pi-getram$(EXT) pi-port$(EXT) \
75 pi-csd$(EXT) pi-nredir$(EXT) \
76@@ -161,6 +162,9 @@
77 pilot-addresses$(EXT): $(PILIB) $(GETOPT) pilot-addresses.o
78 $(CCLINK) pilot-addresses.o $(PILIB) $(GETOPT) -o $@ $(LIBS)
79
80+sync-ldif$(EXT): $(PILIB) $(GETOPT) sync-ldif.o line64.o
81+ $(CCLINK) sync-ldif.o line64.o $(PILIB) $(GETOPT) -o $@ $(LIBS)
82+
83 pilot-dedupe$(EXT): $(PILIB) $(GETOPT) pilot-dedupe.o
84 $(CCLINK) pilot-dedupe.o $(PILIB) $(GETOPT) -o $@ $(LIBS)
85
86@@ -275,10 +279,10 @@
87
88 Tcl: FORCE
89 cd Tcl; $(MAKE)
90-
91+
92 Java: FORCE
93 cd Java; $(MAKE)
94-
95+
96 check test tests: FORCE
97 cd tests; $(MAKE) tests
98
99@@ -326,6 +330,12 @@
100 gcc -o tarball scripts/tarball.c -Iinclude
101 ./tarball
102
103+sync-ldif.usage.h: README.sync-ldif
104+ sed -n \
105+ -e '/_START_SYNC_LDIF_USAGE/,/_END_SYNC_LDIF_USAGE/ { s/^_.*//gp' \
106+ -e 's/^\(.*\)$$/"\1\\n"/gp' \
107+ -e '}' < README.sync-ldif > sync-ldif.usage.h
108+
109 #Depend information starts here. Do not edit the text beyond this point!
110 addresses.o: addresses.c include/pi-source.h include/pi-config.h \
111 include/pi-socket.h include/pi-args.h include/pi-version.h \
112@@ -356,6 +366,7 @@
113 include/pi-config.h include/pi-socket.h include/pi-args.h \
114 include/pi-version.h include/pi-sockaddr.h include/pi-macros.h \
115 include/pi-dlp.h include/pi-hinote.h include/pi-appinfo.h
116+line64.o: line64.c
117 install-memo.o: install-memo.c include/pi-source.h include/pi-config.h \
118 include/pi-socket.h include/pi-args.h include/pi-version.h \
119 include/pi-sockaddr.h include/pi-macros.h include/pi-dlp.h \
120@@ -451,3 +462,8 @@
121 include/pi-socket.h include/pi-args.h include/pi-version.h \
122 include/pi-sockaddr.h include/pi-macros.h include/pi-datebook.h \
123 include/pi-appinfo.h include/pi-dlp.h
124+sync-ldif.o: sync-ldif.c include/pi-source.h include/pi-config.h \
125+ include/pi-socket.h include/pi-args.h include/pi-version.h \
126+ include/pi-sockaddr.h include/pi-macros.h include/pi-dlp.h \
127+ include/pi-address.h include/pi-appinfo.h include/pi-sync.h \
128+ sync-ldif.usage.h
129diff -Nru pilot-link.0.9.3/README.sync-ldif pilot-link.0.9.3.new/README.sync-ldif
130--- pilot-link.0.9.3/README.sync-ldif Thu Jan 1 01:00:00 1970
131+++ pilot-link.0.9.3.new/README.sync-ldif Fri May 28 15:54:22 1999
132@@ -0,0 +1,109 @@
133+_START_SYNC_LDIF_USAGE
134+
135+Usage: sync-ldif [-v] <ldif_file> [<ldif_sync_file> [<ldif_archive_file>]]
136+
137+sync-ldif attempts to synchronize the PalmPilot address book with a
138+Netscape Communicator address book LDIF file.
139+
140+To use it:
141+
142+0. BE SAFE AND BACKUP YOUR PALMPILOT AND YOUR COMMUNICATOR ADDRESS
143+BOOK. To backup you Communicator address book, create a new address
144+book, select all of your old address book entries (i.e. cards) and
145+copy them into the new address book.
146+
147+1. Select 'File/Export' in your Communicator address book and enter a
148+file name such as 'net.ldif'. This is the <ldif_file>.
149+
150+2. Run 'sync-ldif -v <ldif_file>'. It should read <ldif_file>,
151+synchronize with the PalmPilot, and then replace <ldif_file> with a
152+synchronized version suitable for importing into Communicator. It
153+also reads and writes two other files. See below for details. The
154+'-v' option causes 'sync-ldif' to be more verbose and print a short
155+message each time it makes a modification to the PalmPilot or
156+<ldif_file>.
157+
158+3. Delete all of the entries in your Communicator address book. The
159+menu items 'Edit/Select All' and 'Edit/Delete' should do the trick.
160+You made a backup right?
161+
162+4. Select 'File/Import' in your Communicator address book and enter the
163+name of the <ldif_file> you used in step 1.
164+
165+With a little luck, both Communicator and your PalmPilot should now
166+contain entries from both.
167+
168+The first time you run the program, it will do a slow sync and create
169+a directory in your home directory called '.sync-ldif/'. Here it will
170+create and maintain the default <ldif_sync_file> 'sync.ldif' and the
171+default <ldif_archive_file> 'archive.ldif'. If you feel daring, you
172+can override either of these two locations when you run the program.
173+This might be necessary if you have multiple Communicator address
174+books, or multiple PalmPilots.
175+
176+<ldif_sync_file> contains the <ldif_file> that resulted from the last
177+run of the program. It is used to determine what changed in
178+Communicator between syncs. WARNING #1: If you synchronize your
179+PalmPilot address book with something other than <ldif_sync_file>
180+(e.g. another computer, application, or file), you should remove
181+<ldif_sync_file> to force a slow sync. WARNING #2: Do not synchronize
182+multiple PalmPilots with the same <ldif_sync_file>.
183+
184+<ldif_archive_file> contains all entries that you marked for archival
185+on the PalmPilot. The program appends newly archived entries each
186+time it runs. To recover entries from the archive file, use
187+Communicator to import <ldif_archive_file> into a new address book and
188+then copy the desired entries to your main address book.
189+
190+If either your PalmPilot or your Communicator address book becomes
191+corrupted, restore it from backup, remove <ldif_sync_file> to force a
192+slow sync, and run sync-ldif again.
193+
194+FAQ: What is with the entries which start with '!' within Communicator?
195+
196+Short answer: They indicate a potential conflict. Look for another
197+entry with the same name or email. If you find one, do a manual merge
198+if necessary and then delete one. If you don't find a matching
199+entry, do not worry about it. One way or the other, remove the '!'s and
200+everything should be OK.
201+
202+Long answer: If you (a) add an entry on the PalmPilot, (b) modify an
203+existing entry in different ways in Communicator and the PalmPilot, or
204+(c) modify an entry on the PalmPilot such that it has the same name or
205+email as an entry in Communicator which currently exists or has been
206+deleted since the last sync, the sync algorithm tries to create a new
207+entry in Communicator. This new entry must have a unique name and
208+email. If the name or email you want already exists in Communicator,
209+the sync algorithm prepends '!'s to both the email and the name until
210+they are both unique in Communicator.
211+
212+PalmPilot is a registered trademark of 3Com Corporation or its
213+subsidiaries.
214+
215+Netscape and Netscape Communicator are registered trademarks of Netscape
216+Communications Corporation in the United States and other countries.
217+
218+_END_SYNC_LDIF_USAGE
219+_START_SYNC_LDIF_DEVELOPER_NOTES
220+
221+The code is a mess and needs a complete rewrite. It implements and
222+uses the rather complex and now defunct interface defined by
223+'pi-sync.h'. The pilot-link maintainer is understandably reluctant to
224+incorporate this patch because it is based on that defunct interface.
225+I did not realize that the interface was defunct until development of
226+'sync-ldif' was well underway, so I chose to quickly and sloppily
227+finish what I started and rewrite it later. Since I am not likely to
228+have time to do a rewrite in the near future, I decided to release it
229+as a patch so that others could use the functionality.
230+
231+Bottom line: The code seems to work, but it is extremely ugly. It has
232+memory leaks. It is poorly commented. It is poorly structured. It
233+is inefficient. It uses a defunct interface (pi-sync.h). In short,
234+it needs a complete rewrite. DO NOT USE THIS CODE AS AN EXAMPLE OF
235+HOW TO WRITE A CONDUIT. Use it as an example of how NOT to write a
236+conduit. :-)
237+
238+Although I can't promise any help, I encourage you to send bug reports
239+and patches to <sync-ldif@brettle.com>.
240+
241+_END_SYNC_LDIF_DEVELOPER_NOTES
242diff -Nru pilot-link.0.9.3/libsock/sync.c pilot-link.0.9.3.new/libsock/sync.c
243--- pilot-link.0.9.3/libsock/sync.c Sun May 3 05:11:28 1998
244+++ pilot-link.0.9.3.new/libsock/sync.c Fri May 28 15:54:22 1999
245@@ -1,4 +1,7 @@
246 /* sync.c: Pilot synchronization logic
247+ *
248+ * IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
249+ * READ ALL OF THE COMMENTS AT THE TOP OF THIS FILE BEFORE PROCEEDING.
250 *
251 * Code written by Kenneth Albanowski, based on work apparently
252 * Copyright (C) 1996, U.S. Robotics Inc. (Possibly Copyright
253@@ -7,6 +10,8 @@
254 * This file contains no information directly copied from the PC Conduit SDK,
255 * but paraphrases many of the algorithms used in that source.
256 *
257+ * 10 Jan 1999 Dean Brettle <dean@brettle.com>:
258+ * Added implementation of fast-sync and slow-sync algorithms.
259 */
fc942a55 260
ab932e04 261 /* Note: This file currently does nothing useful, and even if completed
262@@ -34,8 +39,22 @@
263 * In keeping with the sprit of the EU practice for reverse engineering, the
264 * interface that I have designed to plug into the Palm algorithms has been
265 * retained.
266+ *
267+ * Revised revised revision (Dean Brettle): I tried to write those
268+ * sections that were deleted. I never saw the original code and I
269+ * don't have a copy of the SDK so copyright issues should not be a
270+ * problem.
271 *
272- */
273+ * Revised revised revised revision (Dean Brettle): I have
274+ * successfully used this module to write a address book conduit for
275+ * Netscape Communicator. HOWEVER, it was a major pain. I would not
276+ * recommend it to anyone else. Moreover, Kenneth has told me that he
277+ * agrees that this interface is too difficult and that it is
278+ * basically no longer supported. I am providing the implementation
279+ * as an unofficial patch, but Kenneth is understandably reluctant to
280+ * include it in the official pilot-link. Hopefully, this will
281+ * discourage people from trying to use this module.
282+ * */
fc942a55 283
ab932e04 284 #include <stdio.h>
285 #include "pi-source.h"
286@@ -45,38 +64,434 @@
287 #define Abstract_sync
288 #include "pi-sync.h"
289
290+#define dprintf if (0) printf
291+
292+typedef int (*SyncAction)(int handle,
293+ int db,
294+ PilotRecord *pilot,
295+ LocalRecord *pc,
296+ struct SyncAbs * s,
297+ int slowsync);
298+
299+static int CreatePC(int handle, int db, PilotRecord *pilot,
300+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
301+ return s->StoreRemote(s, pilot);
302+}
303+
304+static int IfDifferentCreatePC(int handle, int db, PilotRecord *pilot,
305+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
306+ int retval = 0;
307+
308+ if (s->Compare(s, pc, pilot) != 0) {
309+ /* Break the link between the pilot and pc records */
310+ retval = s->SetPilotID(s, pc, 0);
311+ if (retval == 0)
312+ retval = s->SetStatus(s, pc, RecordNew);
313+ /* Create a new record on the PC which matches the
314+ pilot record (including its ID) */
315+ if (retval == 0)
316+ retval = s->StoreRemote(s, pilot);
317+ }
318+
319+ return retval;
320+}
321+
322+static int ReplacePC(int handle, int db, PilotRecord *pilot,
323+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
324+ int retval = 0;
325+
326+ retval = s->StoreRemote(s, pilot);
327+ return retval;
328+}
329+
330+static int ReplacePCAndUndelete(int handle, int db, PilotRecord *pilot,
331+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
332+ int retval = 0;
333+ retval = ReplacePC(handle, db, pilot, pc, s, slowsync);
334+ if (retval == 0) retval = s->SetStatus(s, pc, RecordNothing);
335+ return retval;
336+}
337+
338+static int DeletePC(int handle, int db, PilotRecord *pilot,
339+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
340+ return s->SetStatus(s, pc, RecordDeleted);
341+}
342+
343+static int IfSameDeletePC(int handle, int db, PilotRecord *pilot,
344+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
345+ if (s->Compare(s, pc, pilot) == 0)
346+ return s->SetStatus(s, pc, RecordDeleted);
347+ return 0;
348+}
349+
350+static SyncAction pcActionTable[4][5] = {
351+ /* We are going to try to implement the following behavior:
352+
353+ Action Pilot PC
354+ Record Status Record Status*/
355+ {
356+ NULL, /* Nothing Nothing */
357+ NULL, /* Nothing New */
358+ NULL, /* Nothing Deleted */
359+ NULL, /* Nothing Modified */
360+ CreatePC, /* Nothing Unmatched */
361+ },
362+ /* Note that new pilot records will have a status of Modified not New
363+ so this row will never be used. It is here only for symmetry. */
364+ {
365+ CreatePC, /* New Nothing */
366+ IfDifferentCreatePC, /* New New */
367+ ReplacePCAndUndelete, /* New Deleted */
368+ IfDifferentCreatePC, /* New Modified */
369+ CreatePC, /* New Unmatched */
370+ },
371+ {
372+ DeletePC, /* Deleted Nothing */
373+ NULL, /* Deleted New */
374+ NULL, /* Deleted Deleted */
375+ IfSameDeletePC, /* Deleted Modified */
376+ NULL, /* Deleted Unmatched */
377+ },
378+ {
379+ ReplacePC, /* Modified Nothing */
380+ IfDifferentCreatePC, /* Modified New */
381+ ReplacePCAndUndelete, /* Modified Deleted */
382+ IfDifferentCreatePC, /* Modified Modified */
383+ CreatePC, /* Modified Unmatched */
384+ }
385+};
386+
387+
388+static int CreatePi(int handle, int db, PilotRecord *pilot,
389+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
390+ int retval = 0;
391+ PilotRecord * p = s->Transmit(s,pc);
392+ unsigned long newID;
393+
394+ if (!p) return -1;
395+ p->attr = 0;
396+ if(p->secret)
397+ p->attr |= dlpRecAttrSecret ;
398+ p->ID = 0; /* Make sure a new record is created */
399+ if (retval >= 0)
400+ retval = (dlp_WriteRecord(handle, db, p->attr, p->ID,
401+ p->category, p->record, p->length,
402+ &newID) < 0 );
403+ if (retval >= 0) retval = s->SetPilotID(s, pc, newID);
404+ s->FreeTransmit(s, pc, p);
405+ return retval;
406+}
407+
408+static int IfDifferentCreatePi(int handle, int db, PilotRecord *pilot,
409+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
410+ int retval = 0;
411+
412+ if (s->Compare(s, pc, pilot) != 0) {
413+ if (retval >= 0)
414+ retval = CreatePi(handle, db, pilot, pc, s, slowsync);
415+ }
416+
417+ return retval;
418+}
419+
420+static int ReplacePi(int handle, int db, PilotRecord *pilot,
421+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
422+ int retval = 0;
423+ PilotRecord * p = s->Transmit(s,pc);
424+
425+ if (!p) return -1;
426+ if (!pilot) return -1;
427+ p->attr = 0;
428+ if(p->secret)
429+ p->attr |= dlpRecAttrSecret ;
430+ p->ID = pilot->ID; /* Make sure we replace an existing record */
431+ if (retval >= 0)
432+ retval = (dlp_WriteRecord(handle, db, p->attr, p->ID,
433+ p->category, p->record, p->length,
434+ NULL) < 0 );
435+ s->FreeTransmit(s, pc, p);
436+ return retval;
437+}
438+
439+static int ReplacePiAndUndelete(int handle, int db, PilotRecord *pilot,
440+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
441+ int retval = 0;
442+ /* Calling ReplacePi doesn't "undelete", so instead we let
443+ that record be deleted and create a new one */
444+ retval = CreatePi(handle, db, pilot, pc, s, slowsync);
445+ return retval;
446+}
447+
448+static int DeletePi(int handle, int db, PilotRecord *pilot,
449+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
450+ dprintf("In DeletePi()\n");
451+ if (!pilot) return -1;
452+ dprintf("Deleting %ld\n", pilot->ID);
453+ return dlp_DeleteRecord(handle, db, 0, pilot->ID);
454+}
455+
456+static int IfSameDeletePi(int handle, int db, PilotRecord *pilot,
457+ LocalRecord *pc, struct SyncAbs * s, int slowsync) {
458+ if (s->Compare(s, pc, pilot) == 0) {
459+ if (!pilot) return -1;
460+ return dlp_DeleteRecord(handle, db, 0, pilot->ID);
461+ }
462+ return 0;
463+}
464+
465+static SyncAction pilotActionTable[4][5] = {
466+ /* We are going to try to implement the following behavior:
467+
468+ Action PC Pilot
469+ Record Status Record Status*/
470+ {
471+ NULL, /* Nothing Nothing */
472+ NULL, /* Nothing New */
473+ NULL, /* Nothing Deleted */
474+ NULL, /* Nothing Modified */
475+ CreatePi, /* Nothing Unmatched */
476+ },
477+ {
478+ CreatePi, /* New Nothing */
479+ IfDifferentCreatePi, /* New New */
480+ ReplacePiAndUndelete, /* New Deleted */
481+ IfDifferentCreatePi, /* New Modified */
482+ CreatePi, /* New Unmatched */
483+ },
484+ {
485+ DeletePi, /* Deleted Nothing */
486+ NULL, /* Deleted New */
487+ NULL, /* Deleted Deleted */
488+ NULL, /* Deleted Modified */
489+ NULL, /* Deleted Unmatched */
490+ },
491+ {
492+ ReplacePi, /* Modified Nothing */
493+ IfDifferentCreatePi, /* Modified New */
494+ ReplacePiAndUndelete, /* Modified Deleted */
495+ IfDifferentCreatePi, /* Modified Modified */
496+ CreatePi, /* Modified Unmatched */
497+ }
498+};
499+
500+
501+
502 /* Given a remote (Pilot) record, stored in a PilotRecord structure, determine what,
503 if anything, should be done with it, by looking at its flags, and possibly looking
504 it up in the local database. */
505-int SyncRecord(int handle, int db, PilotRecord * Remote, struct SyncAbs * s, int slowsync) {
506- /* --- Paraphrased code derived from Palm;s Conduit SDK elided --- */
507- abort(); /* For lack of anything better to do */
508- return 0;
509+static int MergeRecordToLocal(int handle, int db, PilotRecord *pilot, struct SyncAbs * s, int slowsync) {
510+ int status = 0;
511+ int retval = 0;
512+ int pcStatus = 0;
513+ int pilotStatus = 0;
514+ SyncAction sa;
515+
516+ LocalRecord *pc = NULL;
517+
518+ /* Find the matching local record, if any. */
519+ status = s->MatchRecord(s, &pc, pilot);
520+
521+ /* Archive the record if requested */
522+ if(pilot->archived) {
523+ retval = s->ArchiveRemote(s, pc, pilot);
524+ }
525+
526+ if (status == 0) {
527+ switch (pc->attr) {
528+ case RecordNothing: pcStatus = 0; break;
529+ case RecordNew: pcStatus = 1; break;
530+ case RecordDeleted: pcStatus = 2; break;
531+ case RecordModified: pcStatus = 3; break;
532+ default: retval = -1;
533+ }
534+ } else {
535+ pcStatus = 4; /* Unmatched */
536+ }
537+
538+
539+ /* For slow syncs, assume that the pilot record changed if
540+ it is different than the PC record, even if it isn't marked as
541+ modified. Note: if the PC record was modified or deleted, this
542+ will result in a new PC record being created. */
543+ if (slowsync) {
544+ if (!pc) {
545+ pilot->attr = RecordNew;
546+ } else if (s->Compare(s, pc, pilot) != 0) {
547+ pilot->attr = RecordModified;
548+ } else
549+ pilot->attr = RecordNothing;
550+ }
551+ switch (pilot->attr) {
552+ case RecordNothing: pilotStatus = 0; break;
553+ case RecordNew: pilotStatus = 1; break;
554+ case RecordDeleted: pilotStatus = 2; break;
555+ case RecordModified: pilotStatus = 3; break;
556+ default: retval = -1;
557+ }
558+
559+ /* Look up the appropriate action */
560+ sa = pcActionTable[pilotStatus][pcStatus];
561+
562+ /* Assuming the action is non-null and we haven't encountered an error
563+ so far, call the action */
564+ if (sa && retval >= 0) {
565+ retval = (*sa)(handle, db, pilot, pc, s, slowsync);
566+ }
567+
568+ /* Let the SyncAbs free memory if necessary */
569+ s->FreeMatch(s, &pc);
570+ return retval;
571+}
572+
573+
574+/* Given a local record, stored in a LocalRecord structure, determine what,
575+ if anything, should be done with it, by looking at its flags, and possibly looking
576+ it up in the pilot. */
577+static int MergeRecordToRemote(int handle, int db, LocalRecord *pc, struct SyncAbs * s, int slowsync) {
578+
579+
580+ int status = 0;
581+ int retval = 0;
582+ int pcStatus = 0;
583+ int pilotStatus = 0;
584+ SyncAction sa;
585+ unsigned char buffer[0xffff];
586+ PilotRecord p;
587+ PilotRecord *pilot = &p;
588+ int index = 0;
589+ long id = 0;
590+
591+ memset(&p, 0, sizeof(PilotRecord));
592+ p.record = buffer;
593+ id = s->GetPilotID(s, pc);
594+ dprintf("id=%ld\n", id);
595+ if (id) {
596+ /* Find the matching remote record, if any. */
597+ status = dlp_ReadRecordById(handle, db, id,
598+ p.record, &index, &p.length, &p.attr,
599+ &p.category);
600+ p.secret = p.attr & dlpRecAttrSecret;
601+ p.archived = p.attr & dlpRecAttrArchived;
602+ if(p.attr & dlpRecAttrDeleted)
603+ p.attr = RecordDeleted;
604+ else if (p.attr & dlpRecAttrDirty)
605+ p.attr = RecordModified;
606+ else
607+ p.attr = RecordNothing;
608+
609+ if (status < 0 && status != dlpErrNotFound) {
610+ fprintf(stderr, "ReadRecordById Error: %s\n", dlp_strerror(status));
611+ return 0;
612+ }
613+ }
614+ pilot->ID = id;
615+
616+ /* Archive the record if requested */
617+ if(pc->archived) {
618+ retval = s->ArchiveLocal(s, pc);
619+ if (retval >= 0) retval = s->ClearStatusArchiveLocal(s, pc);
620+ }
621+
622+ if (status > 0) {
623+ switch (pilot->attr) {
624+ case RecordNothing: pilotStatus = 0; break;
625+ case RecordNew: pilotStatus = 1; break;
626+ case RecordDeleted: pilotStatus = 2; break;
627+ case RecordModified: pilotStatus = 3; break;
628+ default: retval = -1;
629+ }
630+ } else {
631+ pilotStatus = 4; /* Unmatched */
632+ pilot = NULL;
633+ }
634+
635+
636+ /* For slow syncs, assume that the PC record changed if
637+ it is different than the pilot record, even if it isn't marked as
638+ modified. Note: if the pilot record was modified or deleted, this
639+ will result in a new pilot record being created. */
640+ if (slowsync) {
641+ if (!pilot) {
642+ pc->attr = RecordNew;
643+ } else if (s->Compare(s, pc, pilot) != 0) {
644+ pc->attr = RecordModified;
645+ } else
646+ pc->attr = RecordNothing;
647+ }
648+ switch (pc->attr) {
649+ case RecordNothing: pcStatus = 0; break;
650+ case RecordNew: pcStatus = 1; break;
651+ case RecordDeleted: pcStatus = 2; break;
652+ case RecordModified: pcStatus = 3; break;
653+ default: retval = -1;
654+ }
655+
656+ /* Look up the appropriate action */
657+ sa = pilotActionTable[pcStatus][pilotStatus];
658+
659+ dprintf("pcStatus=%d, pilotStatus=%d, retval=%d\n",
660+ pcStatus, pilotStatus, retval);
661+ /* Assuming the action is non-null and we haven't encountered an error
662+ so far, call the action */
663+ if (sa && retval >= 0) {
664+ retval = (*sa)(handle, db, pilot, pc, s, slowsync);
665+ if (retval < 0) {
666+ fprintf(stderr, "pilotAction Error: %s\n", dlp_strerror(retval));
667+ return retval;
668+ }
669+ }
670+
671+ return retval;
672 }
673
674 /* Iterate over local records, copying records to remote, or deleting, or
675 archiving, as flags dictate. This is the last step in any sync. */
676-void MergeToRemote(int handle, int db, struct SyncAbs * s) {
677- /* --- Paraphrased code derived from Palm's Conduit SDK elided --- */
678- abort(); /* For lack of anything better to do */
679- return;
680+static int MergeToRemote(int handle, int db, struct SyncAbs * s, int slowsync) {
681+
682+ LocalRecord *pc = NULL;
683+ int retval = 0;
684+
685+ while (1) {
686+ if (slowsync) {
687+ if (s->Iterate(s, &pc) < 0 || !pc) break;
688+ } else {
689+ /* NOTE: IterateSpecific() just returns the next record
690+ which is new, modified, or deleted. The 3rd and 4th
691+ parameters are ignored */
692+ if (s->IterateSpecific(s, &pc, 0, 0) < 0 || !pc) break;
693+ }
694+ retval = MergeRecordToRemote(handle, db, pc, s, slowsync);
695+ if (retval < 0)
696+ break;
697+ }
698+ s->Purge(s);
699+ return 0;
700 }
701
702-/* Perform a "slow" sync. This requires that the local (PC) has
703- consistent, accurate, and sufficient modification flags. All
704- of the records on the remote (Pilot) are pulled in, and compared
705- for modifications */
706-int SlowSync(int handle, int db, struct SyncAbs * s ) {
707+
708+
709+
710+
711+/* Iterate over remote records, copying records to local, or marking
712+ local records for deletion, or archiving, as flags dictate. */
713+static int MergeToLocal(int handle, int db, struct SyncAbs * s, int slowsync) {
714+ /* --- Paraphrased code derived from Palm's Conduit SDK elided --- */
715 int index = 0;
716 int retval = 0;
717 unsigned char buffer[0xffff];
718 PilotRecord p;
719+
720 p.record = buffer;
721-
722- /* --- Paraphrased code derived from Palm's Conduit SDK elided --- */
723+
724 index = 0;
725
726- while(dlp_ReadRecordByIndex(handle,db, index, p.record, &p.ID, &p.length, &p.attr, &p.category)>=0) {
727+ while(1) {
728+ if (slowsync) {
729+ if (dlp_ReadRecordByIndex(handle,db, index, p.record, &p.ID, &p.length, &p.attr, &p.category) < 0) break;
730+ } else {
731+ if (dlp_ReadNextModifiedRec(handle,db, p.record, &p.ID, &index, &p.length, &p.attr, &p.category) < 0) break;
732+ }
733+
734 p.secret = p.attr & dlpRecAttrSecret;
735 p.archived = p.attr & dlpRecAttrArchived;
736 if(p.attr & dlpRecAttrDeleted)
737@@ -85,11 +500,27 @@
738 p.attr = RecordModified;
739 else
740 p.attr = RecordNothing;
741- SyncRecord(handle, db, &p, s, 1);
742+ retval = MergeRecordToLocal(handle, db, &p, s, slowsync);
743+ if (retval < 0)
744+ break;
745 index++;
746 }
747-
748- MergeToRemote(handle,db,s);
749+ if (retval >= 0) retval = dlp_CleanUpDatabase(handle, db);
750+ if (retval >= 0) retval = dlp_ResetSyncFlags(handle, db);
751+ return retval;
752+}
753+
754+/* Perform a "slow" sync. This requires that the local (PC) has
755+ consistent, accurate, and sufficient modification flags. All
756+ of the records on the remote (Pilot) are pulled in, and compared
757+ for modifications */
758+int SlowSync(int handle, int db, struct SyncAbs * s ) {
759+ int retval = 0;
760+
761+ retval = MergeToLocal(handle, db, s, 1);
762+
763+ if (retval >= 0)
764+ retval = MergeToRemote(handle,db,s, 1);
765
766 return retval;
767 }
768@@ -98,26 +529,12 @@
769 local (PC) have consistent, accurate, and sufficient modification flags.
770 If this is not true, a slow sync should be used */
771 int FastSync(int handle, int db, struct SyncAbs * s ) {
772- int index = 0;
773 int retval = 0;
774- unsigned char buffer[0xffff];
775- PilotRecord p;
776- p.record = buffer;
777-
778- while(dlp_ReadNextModifiedRec(handle,db, p.record, &p.ID, &index, &p.length, &p.attr, &p.category)>=0) {
779- printf("Got a modified record\n");
780- p.secret = p.attr & dlpRecAttrSecret;
781- p.archived = p.attr & dlpRecAttrArchived;
782- if(p.attr & dlpRecAttrDeleted)
783- p.attr = RecordDeleted;
784- else if (p.attr & dlpRecAttrDirty)
785- p.attr = RecordModified;
786- else
787- p.attr = RecordNothing;
788- SyncRecord(handle, db, &p, s, 0);
789- }
790-
791- MergeToRemote(handle,db,s);
792+
793+ retval = MergeToLocal(handle, db, s, 0);
794+
795+ if (retval >= 0)
796+ retval = MergeToRemote(handle,db,s, 0);
797
798 return retval;
799 }
800@@ -126,11 +543,15 @@
801 int CopyToRemote(int handle, int db, struct SyncAbs * s) {
802 LocalRecord * Local = 0;
803 int retval = 0;
804- dlp_DeleteRecord(handle, db, 1, 0);
805+ retval = dlp_DeleteRecord(handle, db, 1, 0);
806+ if (retval < 0) {
807+ fprintf(stderr, "Error deleting all records: %s\n",
808+ dlp_strerror(retval));
809+ return retval;
810+ }
811 while(s->Iterate(s,&Local) && Local) {
812 if (Local->archived) {
813 retval = s->ClearStatusArchiveLocal(s,Local);
814- s->SetStatus(s,Local,RecordDeleted);
815 } else if (Local->attr != RecordDeleted) {
816 PilotRecord * p = s->Transmit(s,Local);
817 s->SetStatus(s,Local,RecordNothing);
818@@ -139,10 +560,11 @@
819 p->attr |= dlpRecAttrSecret ;
820 retval = (dlp_WriteRecord(handle, db, p->attr, p->ID,
821 p->category, p->record, p->length, 0) < 0 );
822+ if (retval < 0) break;
823 s->FreeTransmit(s,Local, p);
824 }
825 }
826- s->Purge(s);
827+ if (retval >= 0) s->Purge(s);
828 return retval;
829 }
830
831diff -Nru pilot-link.0.9.3/line64.c pilot-link.0.9.3.new/line64.c
832--- pilot-link.0.9.3/line64.c Thu Jan 1 01:00:00 1970
833+++ pilot-link.0.9.3.new/line64.c Fri May 28 15:54:22 1999
834@@ -0,0 +1,328 @@
835+/*
836+ Copyright (c) 1992-1996 Regents of the University of Michigan.
837+ All rights reserved.
838+
839+ Redistribution and use in source and binary forms are permitted
840+ provided that this notice is preserved and that due credit is given
841+ to the University of Michigan at Ann Arbor. The name of the University
842+ may not be used to endorse or promote products derived from this
843+ software without specific prior written permission. This software
844+ is provided ``as is'' without express or implied warranty.
845+
846+ This file is derived from line64.c in OpenLDAP which in turn in
847+ derived from the University of Michigan's LDAP source code.
848+
849+ * 11 Jan 1999 Dean Brettle <dean@brettle.com>
850+ - Copied from OpenLDAP into the pilot-link tree for use by the
851+ sync-addresses application.
852+ - Added above copyright notice
853+ - Removed extraneous header files and other references to the other
854+ packages
855+*/
856+
857+/* line64.c - routines for dealing with the slapd line format */
858+
859+#include <stdio.h>
860+#include <stdlib.h>
861+#include <string.h>
862+#include <ctype.h>
863+
864+#define RIGHT2 0x03
865+#define RIGHT4 0x0f
866+#define CONTINUED_LINE_MARKER '\001'
867+
868+#define LINE_WIDTH 76
869+/* VERY conservative estimate of space required for a type-value "line" */
870+#define LDIF_SIZE_NEEDED(t, v) (4*((t)+(v)))
871+
872+static char nib2b64[0x40f] =
873+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
874+
875+static unsigned char b642nib[0x80] = {
876+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
877+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
878+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
879+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
880+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
881+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
882+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
883+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
884+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
885+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
886+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
887+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
888+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
889+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
890+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
891+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
892+};
893+
894+/*
895+ * str_parse_line - takes a line of the form "type:[:] value" and splits it
896+ * into components "type" and "value". if a double colon separates type from
897+ * value, then value is encoded in base 64, and parse_line un-decodes it
898+ * (in place) before returning.
fc942a55 899+ */
ab932e04 900+
901+int
902+str_parse_line(
903+ char *line,
904+ char **type,
905+ char **value,
906+ int *vlen
907+)
908+{
909+ char *p, *s, *d, *byte, *stop;
910+ char nib;
911+ int i, b64;
912+
913+ /* skip any leading space */
914+ while ( isspace( *line ) ) {
915+ line++;
916+ }
917+ *type = line;
918+
919+ for ( s = line; *s && *s != ':'; s++ )
920+ ; /* NULL */
921+ if ( *s == '\0' ) {
922+ fprintf(stderr, "parse_line missing ':'\n");
923+ return( -1 );
924+ }
925+
926+ /* trim any space between type and : */
927+ for ( p = s - 1; p > line && isspace( *p ); p-- ) {
928+ *p = '\0';
929+ }
930+ *s++ = '\0';
931+
932+ /* check for double : - indicates base 64 encoded value */
933+ if ( *s == ':' ) {
934+ s++;
935+ b64 = 1;
936+
937+ /* single : - normally encoded value */
938+ } else {
939+ b64 = 0;
940+ }
941+
942+ /* skip space between : and value */
943+ while ( isspace( *s ) ) {
944+ s++;
945+ }
946+
947+ /* if no value is present, error out */
948+ if ( *s == '\0' ) {
949+ fprintf(stderr, "parse_line missing value for %s\n", *type);
950+ return( -1 );
951+ }
952+
953+ /* check for continued line markers that should be deleted */
954+ for ( p = s, d = s; *p; p++ ) {
955+ if ( *p != CONTINUED_LINE_MARKER )
956+ *d++ = *p;
957+ }
958+ *d = '\0';
959+
960+ *value = s;
961+ if ( b64 ) {
962+ stop = strchr( s, '\0' );
963+ byte = s;
964+ for ( p = s, *vlen = 0; p < stop; p += 4, *vlen += 3 ) {
965+ for ( i = 0; i < 3; i++ ) {
966+ if ( p[i] != '=' && (p[i] & 0x80 ||
967+ b642nib[ p[i] & 0x7f ] > 0x3f) ) {
968+ fprintf(stderr, "invalid base 64 encoding char (%c) 0x%x\n",
969+ p[i], p[i]);
970+ return( -1 );
971+ }
972+ }
973+
974+ /* first digit */
975+ nib = b642nib[ p[0] & 0x7f ];
976+ byte[0] = nib << 2;
977+ /* second digit */
978+ nib = b642nib[ p[1] & 0x7f ];
979+ byte[0] |= nib >> 4;
980+ byte[1] = (nib & RIGHT4) << 4;
981+ /* third digit */
982+ if ( p[2] == '=' ) {
983+ *vlen += 1;
984+ break;
985+ }
986+ nib = b642nib[ p[2] & 0x7f ];
987+ byte[1] |= nib >> 2;
988+ byte[2] = (nib & RIGHT2) << 6;
989+ /* fourth digit */
990+ if ( p[3] == '=' ) {
991+ *vlen += 2;
992+ break;
993+ }
994+ nib = b642nib[ p[3] & 0x7f ];
995+ byte[2] |= nib;
996+
997+ byte += 3;
998+ }
999+ s[ *vlen ] = '\0';
1000+ } else {
1001+ *vlen = (int) (d - s);
1002+ }
1003+
1004+ return( 0 );
1005+}
1006+
1007+/*
1008+ * str_getline - return the next "line" (minus newline) of input from a
1009+ * string buffer of lines separated by newlines, terminated by \n\n
1010+ * or \0. this routine handles continued lines, bundling them into
1011+ * a single big line before returning. if a line begins with a white
1012+ * space character, it is a continuation of the previous line. the white
1013+ * space character (nb: only one char), and preceeding newline are changed
1014+ * into CONTINUED_LINE_MARKER chars, to be deleted later by the
1015+ * str_parse_line() routine above.
1016+ *
1017+ * it takes a pointer to a pointer to the buffer on the first call,
1018+ * which it updates and must be supplied on subsequent calls.
1019+ */
1020+
1021+char *
1022+str_getline( char **next )
1023+{
1024+ char *l;
1025+ char c;
1026+
1027+ if ( *next == NULL || **next == '\n' || **next == '\0' ) {
1028+ return( NULL );
1029+ }
1030+
1031+ l = *next;
1032+ while ( (*next = strchr( *next, '\n' )) != NULL ) {
1033+ c = *(*next + 1);
1034+ if ( isspace( c ) && c != '\n' ) {
1035+ **next = CONTINUED_LINE_MARKER;
1036+ *(*next+1) = CONTINUED_LINE_MARKER;
1037+ } else {
1038+ *(*next)++ = '\0';
1039+ break;
1040+ }
1041+ (*next)++;
1042+ }
1043+
1044+ return( l );
1045+}
1046+
1047+void
1048+put_type_and_value( char **out, char *t, char *val, int vlen )
1049+{
1050+ unsigned char *byte, *p, *stop;
1051+ unsigned char buf[3];
1052+ unsigned long bits;
1053+ char *save;
1054+ int i, b64, pad, len, savelen;
1055+ len = 0;
1056+
1057+ /* put the type + ": " */
1058+ for ( p = (unsigned char *) t; *p; p++, len++ ) {
1059+ *(*out)++ = *p;
1060+ }
1061+ *(*out)++ = ':';
1062+ len++;
1063+ save = *out;
1064+ savelen = len;
1065+ *(*out)++ = ' ';
1066+ b64 = 0;
1067+
1068+ stop = (unsigned char *) (val + vlen);
1069+ if ( isascii( val[0] ) && (isspace( val[0] ) || val[0] == ':') ) {
1070+ b64 = 1;
1071+ } else {
1072+ for ( byte = (unsigned char *) val; byte < stop;
1073+ byte++, len++ ) {
1074+ if ( !isascii( *byte ) || !isprint( *byte ) ) {
1075+ b64 = 1;
1076+ break;
1077+ }
1078+ if ( len > LINE_WIDTH ) {
1079+ *(*out)++ = '\n';
1080+ *(*out)++ = ' ';
1081+ len = 1;
1082+ }
1083+ *(*out)++ = *byte;
1084+ }
1085+ }
1086+ if ( b64 ) {
1087+ *out = save;
1088+ *(*out)++ = ':';
1089+ *(*out)++ = ' ';
1090+ len = savelen + 2;
1091+ /* convert to base 64 (3 bytes => 4 base 64 digits) */
1092+ for ( byte = (unsigned char *) val; byte < stop - 2;
1093+ byte += 3 ) {
1094+ bits = (byte[0] & 0xff) << 16;
1095+ bits |= (byte[1] & 0xff) << 8;
1096+ bits |= (byte[2] & 0xff);
1097+
1098+ for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
1099+ if ( len > LINE_WIDTH ) {
1100+ *(*out)++ = '\n';
1101+ *(*out)++ = ' ';
1102+ len = 1;
1103+ }
1104+
1105+ /* get b64 digit from high order 6 bits */
1106+ *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
1107+ }
1108+ }
1109+
1110+ /* add padding if necessary */
1111+ if ( byte < stop ) {
1112+ for ( i = 0; byte + i < stop; i++ ) {
1113+ buf[i] = byte[i];
1114+ }
1115+ for ( pad = 0; i < 3; i++, pad++ ) {
1116+ buf[i] = '\0';
1117+ }
1118+ byte = buf;
1119+ bits = (byte[0] & 0xff) << 16;
1120+ bits |= (byte[1] & 0xff) << 8;
1121+ bits |= (byte[2] & 0xff);
1122+
1123+ for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
1124+ if ( len > LINE_WIDTH ) {
1125+ *(*out)++ = '\n';
1126+ *(*out)++ = ' ';
1127+ len = 1;
1128+ }
1129+
1130+ if( i + pad < 4 ) {
1131+ /* get b64 digit from low order 6 bits */
1132+ *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
1133+ } else {
1134+ *(*out)++ = '=';
1135+ }
1136+ }
1137+ }
1138+ }
1139+ *(*out)++ = '\n';
1140+}
1141+
1142+
1143+char *
1144+ldif_type_and_value( char *type, char *val, int vlen )
1145+/*
1146+ * return malloc'd, zero-terminated LDIF line
1147+ */
1148+{
1149+ char *buf, *p;
1150+ int tlen;
1151+
1152+ tlen = strlen( type );
1153+ if (( buf = (char *)malloc( LDIF_SIZE_NEEDED( tlen, vlen ) + 1 )) !=
1154+ NULL ) {
1155+ }
1156+
1157+ p = buf;
1158+ put_type_and_value( &p, type, val, vlen );
1159+ *p = '\0';
1160+
1161+ return( buf );
1162+}
1163diff -Nru pilot-link.0.9.3/sync-ldif.c pilot-link.0.9.3.new/sync-ldif.c
1164--- pilot-link.0.9.3/sync-ldif.c Thu Jan 1 01:00:00 1970
1165+++ pilot-link.0.9.3.new/sync-ldif.c Fri May 28 15:54:22 1999
1166@@ -0,0 +1,1661 @@
1167+/* sync-ldif.c: Pilot Netscape Communicator address conduit.
1168+ *
1169+ * This is free software, licensed under the GNU Public License V2.
1170+ * See the file COPYING for details.
1171+ *
1172+ * IMPORTANT!! WARNING!! DANGER!!: Read the Developer Notes in
1173+ * README.sync-ldif before using this code.
1174+ *
1175+ * Code written by Dean Brettle <dean@brettle.com> based on some code
1176+ * by Kenneth Albanowski.
1177+ *
1178+ * */
1179+
1180+#include <stdio.h>
1181+#include <stdlib.h>
1182+#include <assert.h>
1183+
1184+#include <time.h>
1185+
1186+#include "pi-source.h"
1187+#include "pi-socket.h"
1188+#include "pi-dlp.h"
1189+#include "pi-address.h"
1190+#include "pi-sync.h"
1191+
1192+#include <sys/stat.h>
1193+#include <sys/types.h>
1194+#include <fcntl.h>
1195+#include <unistd.h>
1196+
1197+#define PILOTPORT "/dev/pilot"
1198+
1199+static int sync_ldif_debug = 0;
1200+/* Debugging printf */
1201+#define dprintf if (sync_ldif_debug) printf
1202+#define d2printf if (sync_ldif_debug > 1) printf
1203+#define d2EntryPrint if (sync_ldif_debug > 1) EntryPrint
1204+#define d2AttributePrint if (sync_ldif_debug > 1) AttributePrint
1205+
1206+/* The following functions are defined in line64.c */
1207+int
1208+str_parse_line(
1209+ char *line,
1210+ char **type,
1211+ char **value,
1212+ int *vlen
1213+);
1214+
1215+char *
1216+str_getline( char **next );
1217+
1218+char *
1219+ldif_type_and_value( char *type, char *val, int vlen );
1220+
1221+
1222+/* Data structures used by sync-addresses */
1223+
1224+typedef struct {
1225+ int status;
1226+ char *type, *value;
1227+ int value_length;
1228+} Attribute;
1229+
1230+/* Define our own LocalRecord type. */
1231+typedef struct LocalRecord {
1232+ StandardLocalRecord;
1233+ long ID;
1234+ time_t mtime;
1235+ Attribute **attrs;
1236+ int num_attrs;
1237+ int num_attrs_allocated;
1238+} Entry;
1239+
1240+typedef struct {
1241+ Entry **entries;
1242+ int num_entries;
1243+ int num_entries_allocated;
1244+} Database;
1245+
1246+/* Define our own SyncAbs type */
1247+struct SyncAbs {
1248+ StandardSyncAbs;
1249+ Database **db_p;
1250+ Database **db_archive_p;
1251+ struct AddressAppInfo *aai;
1252+ int slow_sync;
1253+ int sd;
1254+};
1255+
1256+/* Functions */
1257+
1258+char * StringAppend(char **dest_p, char *src)
1259+{
1260+ assert(dest_p && src);
1261+ if (!*dest_p) {
1262+ *dest_p = calloc(strlen(src)+1, sizeof(char));
1263+ } else {
1264+ *dest_p = realloc(*dest_p, strlen(*dest_p)+strlen(src)+1);
1265+ }
1266+ assert(*dest_p);
1267+ strcat(*dest_p, src);
1268+ return *dest_p;
1269+}
1270+
1271+
1272+void AttributePrint(Attribute *attr, FILE *fp)
1273+{
1274+ char *line
1275+ = ldif_type_and_value( attr->type, attr->value, attr->value_length);
1276+
1277+ assert(line != NULL);
1278+ fputs(line, fp);
1279+ free(line);
1280+}
1281+
1282+void EntryPrint(Entry *entry, FILE *fp)
1283+{
1284+ int a;
1285+
1286+ for (a = 0; a < entry->num_attrs; a++) {
1287+ Attribute *attr = entry->attrs[a];
1288+ AttributePrint(attr, fp);
1289+ }
1290+}
1291+
1292+int EntryFindAttribute(int *a_p, Entry *entry, char *type)
1293+{
1294+ int a = 0;
1295+
1296+ assert(a_p);
1297+ if (!entry) return -1;
1298+ for (a = *a_p; a < entry->num_attrs; a++) {
1299+ Attribute *attr = entry->attrs[a];
1300+ if (strcmp(attr->type, type) == 0) break;
1301+ }
1302+ *a_p = a;
1303+ if (a < entry->num_attrs)
1304+ return a;
1305+ else
1306+ return -1;
1307+}
1308+
1309+
1310+Entry *EntryAppendAttribute(Entry **entry_p, Attribute *attr)
1311+{
1312+ const int chunk_size = 10;
1313+ const int growth_factor = 2;
1314+ Entry *e = NULL;
1315+
1316+ assert(entry_p && attr);
1317+ if (!*entry_p) {
1318+ *entry_p = calloc(1, sizeof(Entry));
1319+ }
1320+ e = *entry_p;
1321+ if (!e->attrs) {
1322+ e->attrs = calloc(chunk_size, sizeof(Attribute*));
1323+ e->num_attrs_allocated = chunk_size;
1324+ }
1325+
1326+ assert(e->attrs);
1327+ if (e->num_attrs == e->num_attrs_allocated) {
1328+ e->num_attrs_allocated *= growth_factor;
1329+ e->attrs = realloc(e->attrs,
1330+ e->num_attrs_allocated * sizeof(Attribute*));
1331+ memset(e->attrs + e->num_attrs, 0,
1332+ (e->num_attrs_allocated - e->num_attrs) * sizeof(Attribute*));
1333+ }
1334+ e->attrs[e->num_attrs] = attr;
1335+ e->num_attrs++;
1336+ *entry_p = e;
1337+ return *entry_p;
1338+}
1339+
1340+
1341+void EntryDeleteAttribute(Entry *entry, int a)
1342+{
1343+ Attribute *attribute = NULL;
1344+
1345+ assert(entry);
1346+ assert(entry->attrs);
1347+ assert(a < entry->num_attrs);
1348+ assert(entry->attrs[a]);
1349+
1350+ attribute = entry->attrs[a];
1351+ free(attribute->type);
1352+ free(attribute->value);
1353+ free(attribute);
1354+ memmove(entry->attrs + a, entry->attrs + a + 1,
1355+ (entry->num_attrs - a - 1) * sizeof(Attribute *));
1356+ entry->num_attrs--;
1357+}
1358+
1359+
1360+Entry *EntrySetAttribute(Entry **entry_p, char *name, char *value)
1361+{
1362+ Entry *entry = NULL;
1363+ Attribute *attr = NULL;
1364+ int a = 0;
1365+
1366+ assert(entry_p);
1367+ assert(name);
1368+ entry = *entry_p;
1369+
1370+ if (EntryFindAttribute(&a, entry, name) < 0) {
1371+ if (!value) {
1372+ return entry;
1373+ }
1374+ attr = (Attribute *)calloc(1, sizeof(Attribute));
1375+ entry = EntryAppendAttribute(entry_p, attr);
1376+ attr->type = strdup(name);
1377+ } else {
1378+ if (!value) {
1379+ EntryDeleteAttribute(entry, a);
1380+ return entry;
1381+ }
1382+ attr = entry->attrs[a];
1383+ free(attr->value);
1384+ }
1385+ attr->value = strdup(value);
1386+ attr->value_length = strlen(value);
1387+
1388+ return entry;
1389+}
1390+
1391+Database *DatabaseCreate()
1392+{
1393+ Database *db = NULL;
1394+ const int chunk_size = 10;
1395+
1396+ db = calloc(1, sizeof(Database));
1397+ db->entries = calloc(chunk_size, sizeof(Entry*));
1398+ db->num_entries_allocated = chunk_size;
1399+ return db;
1400+}
1401+
1402+Database *DatabaseAppendEntry(Database **db_p, Entry *entry)
1403+{
1404+ const int growth_factor = 2;
1405+ Database *db = NULL;
1406+
1407+ assert(db_p && entry);
1408+ if (!*db_p) {
1409+ *db_p = DatabaseCreate();
1410+ }
1411+ db = *db_p;
1412+
1413+ assert(db->entries);
1414+ if (db->num_entries == db->num_entries_allocated) {
1415+ db->num_entries_allocated *= growth_factor;
1416+ db->entries = realloc(db->entries,
1417+ db->num_entries_allocated * sizeof(Entry*));
1418+ memset(db->entries + db->num_entries, 0,
1419+ (db->num_entries_allocated - db->num_entries) * sizeof(Entry*));
1420+ }
1421+ db->entries[db->num_entries] = entry;
1422+ db->num_entries++;
1423+ *db_p = db;
1424+
1425+ return *db_p;
1426+}
1427+
1428+
1429+void DatabaseDeleteEntry(Database *db, int e)
1430+{
1431+ Entry *entry = NULL;
1432+
1433+ assert(db);
1434+ assert(db->entries);
1435+ assert(e < db->num_entries);
1436+ assert(db->entries[e]);
1437+
1438+ entry = db->entries[e];
1439+ while (entry->num_attrs)
1440+ EntryDeleteAttribute(entry, 0);
1441+ free(entry);
1442+ memmove(db->entries + e, db->entries + e + 1,
1443+ (db->num_entries - e - 1) * sizeof(Entry *));
1444+ db->num_entries--;
1445+}
fc942a55 1446+
ab932e04 1447+
1448+Database *ReadLdif(char *filename)
1449+{
1450+ Entry *entry;
1451+ FILE *fp = NULL;
1452+ int buf_length = 4096;
1453+ char buf[buf_length];
1454+ char *ldif_string = NULL;
1455+ char *line = NULL;
1456+ char *p = NULL;
1457+ Database *db = NULL;
1458+
1459+ if (!(fp = fopen(filename, "r"))) {
1460+ return NULL;
1461+ }
1462+
1463+ db = DatabaseCreate();
1464+ /* Read the whole file into a string */
1465+ while (!feof(fp)) {
1466+ int n_bytes = fread(buf, 1, buf_length-1, fp);
1467+ if (n_bytes > 0) {
1468+ buf[n_bytes] = 0;
1469+ StringAppend(&ldif_string, buf);
1470+ }
1471+ }
1472+
1473+ /* str_getline() takes a pointer to a pointer to the buffer on the
1474+ first call, which it updates and must be supplied on subsequent
1475+ calls. */
1476+ p = ldif_string;
1477+ while (p && *p) {
1478+ /* Skip any leading blank lines */
1479+ while (*p /* not eof */
1480+ && (line = str_getline(&p)) == NULL /* blank */)
1481+ ;
1482+ entry = NULL;
1483+ while (line) {
1484+ Attribute *attr;
1485+
1486+ if (line[0] == '#') continue; /* Skip comments */
1487+ attr = (Attribute *)calloc(1, sizeof(Attribute));
1488+ if (str_parse_line(line,
1489+ &(attr->type), &(attr->value), &(attr->value_length))
1490+ < 0) {
1491+ exit(1);
1492+ }
1493+ /* We need our own separately allocated copies of the type and value */
1494+ attr->type = strdup(attr->type);
1495+ attr->value = strdup(attr->value);
1496+ EntryAppendAttribute(&entry, attr);
1497+ line = str_getline(&p); /* get the next line */
1498+ }
1499+ if (entry) DatabaseAppendEntry(&db, entry);
1500+ while (*p == '\n') p++; /* Skip trailing blank lines */
1501+ }
1502+ fclose(fp);
1503+ return db;
1504+}
1505+
1506+void WriteLdif(Database *db, char *filename)
1507+{
1508+ FILE *fp;
1509+ int e;
1510+
1511+ assert(db != NULL);
1512+
1513+ if (!(fp = fopen(filename, "w"))) {
1514+ perror("Unable to open file");
1515+ exit(1);
1516+ }
1517+
1518+ for (e = 0; e < db->num_entries; e++) {
1519+ Entry *entry = db->entries[e];
1520+
1521+ if (e > 0) {
1522+ fputc('\n', fp); /* Blank line to separate entries */
1523+ }
1524+ EntryPrint(entry, fp);
1525+ }
1526+ fclose(fp);
1527+}
1528+
1529+Entry *DatabaseFindEntryMatchAttribute(Database *dbs, Entry *entry, char *type)
1530+{
1531+ int se=0, a=0;
1532+ Attribute *attr=NULL;
1533+
1534+ assert(dbs);
1535+ assert(entry);
1536+ assert(type);
1537+
1538+ if (EntryFindAttribute(&a, entry, type) >= 0) {
1539+ attr = entry->attrs[a];
1540+ }
1541+ if (!attr) return NULL;
1542+
1543+ for (se = 0; se < dbs->num_entries; se++) {
1544+ Entry *sync_entry = dbs->entries[se];
1545+
1546+ a = 0;
1547+ while (EntryFindAttribute(&a, sync_entry, type) >= 0) {
1548+ Attribute *sync_attr = sync_entry->attrs[a];
1549+ if (sync_attr->value_length == attr->value_length
1550+ && memcmp(sync_attr->value, attr->value, attr->value_length) == 0) {
1551+ return sync_entry;
1552+ }
1553+ a++;
1554+ }
1555+ }
1556+
1557+ return NULL;
1558+}
1559+
1560+
1561+Entry *DatabaseFindEntry(Database *dbs, Entry *entry)
1562+{
1563+ Entry *sync_entry = NULL;
1564+
1565+ sync_entry = DatabaseFindEntryMatchAttribute(dbs, entry, "dn");
1566+ if (sync_entry) return sync_entry;
1567+
1568+ sync_entry = DatabaseFindEntryMatchAttribute(dbs, entry, "mail");
1569+ if (sync_entry) return sync_entry;
1570+
1571+ sync_entry = DatabaseFindEntryMatchAttribute(dbs, entry, "cn");
1572+ if (sync_entry) return sync_entry;
1573+
1574+ return NULL;
1575+}
1576+
1577+Attribute *AttributeCopy(Attribute *attr)
1578+{
1579+ Attribute *new_attr = NULL;
1580+
1581+ assert(attr);
1582+ new_attr = calloc(1, sizeof(Attribute));
1583+ new_attr->type = strdup(attr->type);
1584+ /* Leave enough space for the \0 */
1585+ new_attr->value = malloc(attr->value_length+1);
1586+ new_attr->value_length = attr->value_length;
1587+ memcpy(new_attr->value, attr->value, attr->value_length+1);
1588+
1589+ return new_attr;
1590+}
1591+
1592+Entry *EntryCopy(Entry *entry)
1593+{
1594+ Entry *new_entry = NULL;
1595+ int a = 0;
1596+
1597+ assert(entry);
1598+
fc942a55 1599+ for (a = 0; a < entry->num_attrs; a++) {
ab932e04 1600+ EntryAppendAttribute(&new_entry, AttributeCopy(entry->attrs[a]));
1601+ }
1602+ return new_entry;
1603+}
1604+
1605+char *EntryGetAttribute(Entry *entry, char *type)
1606+{
1607+ int a = 0;
1608+ if (EntryFindAttribute(&a, entry, type) == -1) return NULL;
1609+ return entry->attrs[a]->value;
1610+}
1611+
1612+/* These are the important functions below. They implement the interface that
1613+ the abstract synchronization layer invokes */
1614+
1615+/* Get a Pilot ID for a local record, or 0 if no Pilot ID has been set. Any
1616+ local ID mechanism is not relevent, only IDs given by the Pilot. */
1617+unsigned long GetPilotID(SyncAbs * thisSA,LocalRecord * entry)
1618+{
1619+ int a = 0;
1620+
1621+ assert(entry);
1622+ EntryFindAttribute(&a, entry, "xpilot-id");
1623+ if (a < entry->num_attrs) {
1624+ return atol(entry->attrs[a]->value);
1625+ } else {
1626+ return 0;
1627+ }
1628+}
1629+
1630+/* Set the ID on a local record to match a given Pilot ID. */
1631+int SetPilotID(SyncAbs * thisSA, LocalRecord *entry, unsigned long ID)
1632+{
1633+ char buf [40];
1634+
1635+ assert(entry);
1636+ sprintf(buf, "%ld", ID);
1637+ EntrySetAttribute(&entry, "xpilot-id", buf);
1638+ return 0;
1639+}
1640+
1641+/* Free up the LocalRecord returned by MatchRecord */
1642+int FreeMatch(SyncAbs * thisSA, LocalRecord ** Local)
1643+{
1644+ assert(Local);
1645+ *Local = NULL;
1646+ return 0;
1647+}
1648+
1649+/* Iterate over all LocalRecords, in arbitrary order */
1650+int Iterate(SyncAbs * thisSA, LocalRecord ** Local)
1651+{
1652+ LocalRecord *entry = NULL;
1653+ Database *db = NULL;
1654+
1655+ assert(thisSA);
1656+ assert(thisSA->db_p);
1657+ assert(Local);
1658+
1659+ db = *(thisSA->db_p);
1660+ if (!db) {
1661+ *Local = NULL;
1662+ return 0;
1663+ }
1664+
1665+ entry = *Local;
1666+ if( !entry) {
1667+ entry = db->entries[0];
1668+ } else {
1669+ int e = 0;
1670+
1671+ /* Look for the current entry */
1672+ for (e = 0; e < db->num_entries; e++) {
1673+ if (entry == db->entries[e])
1674+ break;
1675+ }
1676+ /* Move to the next one */
1677+ e++;
1678+ if (e < db->num_entries)
1679+ entry = db->entries[e];
1680+ else
1681+ entry = NULL;
1682+ }
1683+ *Local = entry;
1684+ return (entry != NULL);
1685+}
1686+
1687+/* NOTE: IterateSpecific() just returns the next record
1688+ which is new, modified, or deleted. The 3rd and 4th
1689+ parameters are ignored */
1690+int IterateSpecific(SyncAbs * thisSA, LocalRecord ** Local,
1691+ int flag, int archived)
1692+{
1693+ LocalRecord *entry = NULL;
1694+ Database *db = NULL;
1695+ int e = 0;
1696+
1697+ assert(thisSA);
1698+ assert(thisSA->db_p);
1699+ assert(Local);
1700+
1701+ db = *(thisSA->db_p);
1702+ if (!db) {
1703+ *Local = NULL;
1704+ return 0;
1705+ }
1706+
1707+ entry = *Local;
1708+ if( !entry) {
1709+ entry = db->entries[0];
1710+ } else {
1711+
1712+ /* Look for the current entry */
1713+ for (e = 0; e < db->num_entries; e++) {
1714+ if (entry == db->entries[e])
1715+ break;
1716+ }
1717+ /* Move to the next one */
1718+ e++;
1719+ if (e < db->num_entries)
1720+ entry = db->entries[e];
1721+ else
1722+ entry = NULL;
1723+ }
1724+ while (entry && entry->attr == RecordNothing) {
1725+ e++;
1726+ if (e < db->num_entries)
1727+ entry = db->entries[e];
1728+ else
1729+ entry = NULL;
1730+ }
1731+
1732+ *Local = entry;
1733+
1734+ if (entry) {
1735+ char *dn = EntryGetAttribute(entry, "dn");
1736+ if (entry->attr == RecordNew) {
1737+ dprintf("Adding the following record to the pilot:\n\t");
1738+ }
1739+ if (entry->attr == RecordModified) {
1740+ dprintf("Modifying the following record on the pilot:\n\t");
1741+ }
1742+ if (entry->attr == RecordDeleted) {
1743+ dprintf("Deleting the following record on the pilot:\n\t");
1744+ }
1745+ dprintf("%s\n\n", dn?dn:"No dn attribute");
1746+ }
1747+ return (entry != NULL);
1748+}
1749+
1750+/* Set status of local record */
1751+int SetStatus(SyncAbs * thisSA,LocalRecord * Local, int status) {
1752+ Local->attr = status;
1753+ d2printf("Changed the status on the following record to %d:\n", status);
1754+ d2EntryPrint((Entry *)Local, stdout);
1755+ d2printf("\n");
1756+ return 0;
1757+}
1758+
1759+/* There is no GetStatus, the abstract layer used Local->attr */
1760+
1761+/* Set archival status of local record */
1762+int SetArchived(SyncAbs * thisSA,LocalRecord * Local,int archived) {
1763+ Local->archived = archived;
1764+ return 0;
1765+}
1766+
1767+/* There is no GetStatus, the abstract layer used Local->archived */
1768+
1769+char *PilotGetPhone(struct AddressAppInfo *aai,
1770+ struct Address *ai, char *name)
1771+{
1772+ int p = 0;
1773+
1774+ assert(aai);
1775+ assert(ai);
1776+ for (p = 0; p < 5; p++) {
1777+ if (strcmp(aai->phoneLabels[ai->phoneLabel[p]], name) == 0) break;
1778+ }
1779+ if (p >= 5) return NULL;
1780+ return (ai->entry[entryPhone1 + p]);
1781+}
1782+
1783+
1784+int PilotFindLabel(struct AddressAppInfo *aai,
1785+ char *name)
1786+{
1787+ int p = 0;
1788+
1789+ assert(aai);
1790+ if (!name) return 0;
1791+ for (p = 0; p < 8; p++) {
1792+ if (strcmp(aai->phoneLabels[p], name) == 0) break;
1793+ }
1794+ if (p >= 8) return -1;
1795+ return p;
1796+}
1797+
1798+
1799+
1800+/* Given a PilotRecord, try and find a local record with a matching ID */
1801+int MatchRecord(SyncAbs * thisSA, LocalRecord ** Local, PilotRecord * p)
1802+{
1803+ Database *db = NULL;
1804+ Entry theEntry;
1805+ Entry *entry = NULL;
1806+ Attribute theAttr;
1807+ Attribute *attr = NULL;
1808+ char buf[40];
1809+
1810+ assert(thisSA);
1811+ assert(thisSA->db_p);
1812+
1813+ db = *(thisSA->db_p);
1814+ if (!db) {
1815+ *Local = NULL;
1816+ return -1;
1817+ }
1818+
1819+ entry = &theEntry;
1820+ memset(entry, 0, sizeof(Entry));
1821+ attr = &theAttr;
1822+ attr->type = strdup("xpilot-id");
1823+ sprintf(buf, "%ld", p->ID);
1824+ attr->value = buf;
1825+ attr->value_length = strlen(attr->value);
1826+ EntryAppendAttribute(&entry, attr);
1827+ *Local = DatabaseFindEntryMatchAttribute(db, entry, "xpilot-id");
1828+ if (*Local == NULL && thisSA->slow_sync) {
1829+ struct Address ai;
1830+ struct AddressAppInfo *aai = NULL;
1831+ char *gn = NULL;
1832+ char *sn = NULL;
1833+ char *cn = NULL;
1834+ char *mail = NULL;
1835+
1836+ Entry tmpEntryStruct;
1837+ Entry *tmpEntry = &tmpEntryStruct;
1838+
1839+ aai = thisSA->aai;
1840+ unpack_Address(&ai, p->record, p->length);
1841+
1842+ gn = ai.entry[entryFirstname];
1843+ sn = ai.entry[entryLastname];
1844+ mail = PilotGetPhone(aai, &ai, "E-mail");
1845+ if (gn || sn) {
1846+ if (gn) {
1847+ StringAppend(&cn, gn);
1848+ }
1849+ if (sn) {
1850+ if (gn) StringAppend(&cn, " ");
1851+ StringAppend(&cn, sn);
1852+ }
1853+ } else if (mail) {
1854+ StringAppend(&cn, mail);
1855+ }
1856+
1857+ memset(tmpEntry, 0, sizeof(Entry));
1858+ EntrySetAttribute(&tmpEntry, "mail", mail);
1859+ EntrySetAttribute(&tmpEntry, "cn", cn);
1860+
1861+ *Local = DatabaseFindEntry(db, tmpEntry);
1862+ if (*Local) {
1863+ SetPilotID(thisSA, *Local, p->ID);
1864+ }
1865+ }
1866+
1867+ return ((*Local == NULL) ? -1 : 0);
1868+}
1869+
1870+/* Given a PilotRecord, store it in the local database */
1871+int StoreRemote(SyncAbs * thisSA, PilotRecord* p) {
1872+ LocalRecord *entry = NULL;
1873+ struct Address ai;
1874+ Database **db_p = NULL;
1875+ struct AddressAppInfo *aai = NULL;
1876+ char buf[40];
1877+ long new_ID = 0;
1878+ char *mail = NULL;
1879+
1880+ assert(thisSA);
1881+ db_p = thisSA->db_p;
1882+ aai = thisSA->aai;
1883+ assert(aai);
1884+
1885+ if (p->ID != 0) {
1886+ /* possible replacement record */
1887+ if (MatchRecord(thisSA, &entry, p) == -1) {
1888+ new_ID = p->ID;
1889+ }
1890+ }
1891+
1892+ unpack_Address(&ai, p->record, p->length);
1893+
1894+ {
1895+ char *gn = ai.entry[entryFirstname];
1896+ char *sn = ai.entry[entryLastname];
1897+ char *cn = NULL;
1898+ char *dn = NULL;
1899+ char timestamp[32];
1900+
1901+ mail = PilotGetPhone(aai, &ai, "E-mail");
1902+ if (gn || sn) {
1903+ if (gn) {
1904+ StringAppend(&cn, gn);
1905+ }
1906+ if (sn) {
1907+ if (gn) StringAppend(&cn, " ");
1908+ StringAppend(&cn, sn);
1909+ }
1910+ } else if (mail) {
1911+ StringAppend(&cn, mail);
1912+ }
1913+
1914+ /* If this is a new entry and there is already an entry with the
1915+ same "mail" or "cn" we should use a new "mail" and "cn" because
1916+ Netscape needs them to be unique */
1917+ {
1918+ Entry tmpEntryStruct;
1919+ Entry *tmpEntry = &tmpEntryStruct;
1920+
1921+ memset(tmpEntry, 0, sizeof(Entry));
1922+ EntrySetAttribute(&tmpEntry, "mail", mail);
1923+ EntrySetAttribute(&tmpEntry, "cn", cn);
1924+
1925+ while ((p->ID == 0 || new_ID != 0)
1926+ && DatabaseFindEntry(*db_p, tmpEntry)) {
1927+ char *new_cn = NULL;
1928+ char *new_mail = NULL;
1929+
1930+ StringAppend(&new_cn, "!");
1931+ if (cn) {
1932+ StringAppend(&new_cn, cn);
1933+ free(cn);
1934+ }
1935+ cn = new_cn;
1936+
1937+ StringAppend(&new_mail, "!");
1938+ if (mail) {
1939+ StringAppend(&new_mail, mail);
1940+ /* Don't free(mail) because it was returned by PilotGetPhone */
1941+ }
1942+ mail = new_mail;
1943+ EntrySetAttribute(&tmpEntry, "mail", mail);
1944+ EntrySetAttribute(&tmpEntry, "cn", cn);
1945+ }
1946+ while (tmpEntry->num_attrs)
1947+ EntryDeleteAttribute(tmpEntry, 0);
1948+ }
1949+
1950+ if (cn) {
1951+ StringAppend(&dn, "cn=");
1952+ StringAppend(&dn, cn);
1953+ if (mail) {
1954+ StringAppend(&dn, ",mail=");
1955+ StringAppend(&dn, mail);
1956+ }
1957+ } else {
1958+ StringAppend(&dn, "x-dn=missing-cn-and-missing-mail");
1959+ }
1960+ {
1961+ time_t cur_time = time(NULL);
1962+ strftime(timestamp, 32, "%Y%m%d%H%M%SZ", gmtime(&cur_time));
1963+ }
1964+
1965+ EntrySetAttribute(&entry, "dn", dn);
1966+ EntrySetAttribute(&entry, "modifytimestamp", timestamp);
1967+ EntrySetAttribute(&entry, "cn", cn);
1968+ }
1969+
1970+ EntrySetAttribute(&entry, "sn", ai.entry[entryLastname]);
1971+ EntrySetAttribute(&entry, "givenname", ai.entry[entryFirstname]);
1972+ EntrySetAttribute(&entry, "o", ai.entry[entryCompany]);
1973+ EntrySetAttribute(&entry, "streetaddress", ai.entry[entryAddress]);
1974+ EntrySetAttribute(&entry, "locality", ai.entry[entryCity]);
1975+ EntrySetAttribute(&entry, "st", ai.entry[entryState]);
1976+ EntrySetAttribute(&entry, "postalcode", ai.entry[entryZip]);
1977+ EntrySetAttribute(&entry, "countryname", ai.entry[entryCountry]);
1978+ EntrySetAttribute(&entry, "title", ai.entry[entryTitle]);
1979+ EntrySetAttribute(&entry, "description", ai.entry[entryNote]);
1980+ EntrySetAttribute(&entry, "xpilot-custom1", ai.entry[entryCustom1]);
1981+ EntrySetAttribute(&entry, "xpilot-custom2", ai.entry[entryCustom2]);
1982+ EntrySetAttribute(&entry, "xpilot-custom3", ai.entry[entryCustom3]);
1983+ EntrySetAttribute(&entry, "xpilot-custom4", ai.entry[entryCustom4]);
1984+ EntrySetAttribute(&entry, "xpilot-phone1", ai.entry[entryPhone1]);
1985+ EntrySetAttribute(&entry, "xpilot-phone2", ai.entry[entryPhone2]);
1986+ EntrySetAttribute(&entry, "xpilot-phone3", ai.entry[entryPhone3]);
1987+ EntrySetAttribute(&entry, "xpilot-phone4", ai.entry[entryPhone4]);
1988+ EntrySetAttribute(&entry, "xpilot-phone5", ai.entry[entryPhone5]);
1989+ EntrySetAttribute(&entry, "xpilot-phonelabel1",
1990+ aai->phoneLabels[ai.phoneLabel[0]]);
1991+ EntrySetAttribute(&entry, "xpilot-phonelabel2",
1992+ aai->phoneLabels[ai.phoneLabel[1]]);
1993+ EntrySetAttribute(&entry, "xpilot-phonelabel3",
1994+ aai->phoneLabels[ai.phoneLabel[2]]);
1995+ EntrySetAttribute(&entry, "xpilot-phonelabel4",
1996+ aai->phoneLabels[ai.phoneLabel[3]]);
1997+ EntrySetAttribute(&entry, "xpilot-phonelabel5",
1998+ aai->phoneLabels[ai.phoneLabel[4]]);
1999+ sprintf(buf, "%d", ai.showPhone);
2000+ EntrySetAttribute(&entry, "xpilot-showphone", buf);
2001+ EntrySetAttribute(&entry, "telephonenumber", PilotGetPhone(aai, &ai, "Work"));
2002+ EntrySetAttribute(&entry, "xmozillaanyphone", PilotGetPhone(aai, &ai, "Work"));
2003+ EntrySetAttribute(&entry, "homephone", PilotGetPhone(aai, &ai, "Home"));
2004+ EntrySetAttribute(&entry, "facsimiletelephonenumber",
2005+ PilotGetPhone(aai, &ai, "Fax"));
2006+ EntrySetAttribute(&entry, "pagerphone", PilotGetPhone(aai, &ai, "Pager"));
2007+ EntrySetAttribute(&entry, "cellphone", PilotGetPhone(aai, &ai, "Mobile"));
2008+ EntrySetAttribute(&entry, "mail", mail);
2009+
2010+ sprintf(buf, "%d", p->category);
2011+ EntrySetAttribute(&entry, "xpilot-categoryID", buf);
2012+
2013+ /*
2014+ EntrySetAttribute(&entry, "TBD", PilotGetPhone(aai, &ai, "Other"));
2015+ EntrySetAttribute(&entry, "TBD", PilotGetPhone(aai, &ai, "Main"));
2016+ */
2017+
2018+ if (p->ID == 0 || new_ID != 0) {
2019+ /* new record */
2020+ Attribute oc;
2021+ memset(&oc, 0, sizeof(Attribute));
2022+ oc.type = "objectclass";
2023+ oc.value = "top";
2024+ oc.value_length = strlen(oc.value);
2025+ EntryAppendAttribute(&entry, AttributeCopy(&oc));
2026+ oc.value = "person";
2027+ oc.value_length = strlen(oc.value);
2028+ EntryAppendAttribute(&entry, AttributeCopy(&oc));
2029+
2030+ EntrySetAttribute(&entry, "xmozillausehtmlmail", "FALSE");
2031+ EntrySetAttribute(&entry, "xmozillauseconferenceserver", "0");
2032+
2033+ if (new_ID != 0) {
2034+ thisSA->SetPilotID(thisSA, entry, new_ID);
2035+ }
2036+
2037+ DatabaseAppendEntry(db_p, entry);
2038+ thisSA->db_p = db_p;
2039+ }
2040+
2041+
2042+ if (p->attr == RecordDeleted || p->archived)
2043+ entry->attr = RecordDeleted;
2044+ else
2045+ entry->attr = RecordNothing;
2046+
2047+ entry->secret = p->secret;
2048+ entry->archived = p->archived;
2049+
2050+ free_Address(&ai);
2051+
2052+ if (entry) {
2053+ char *dn = EntryGetAttribute(entry, "dn");
2054+ if (p->attr == RecordDeleted) {
2055+ dprintf("Scheduling the following record for deletion on the PC:\n\t");
2056+ } else if (p->attr == RecordNew || p->ID == 0 || new_ID != 0) {
2057+ dprintf("Adding the following record to the PC:\n\t");
2058+ } else if (p->attr == RecordModified) {
2059+ dprintf("Modifying the following record on the PC:\n\t");
2060+ }
2061+ if (p->archived) {
2062+ dprintf("Scheduling the following record for archival on the PC:\n\t");
2063+ }
2064+ dprintf("%s\n\n", dn?dn:"No dn attribute");
2065+ }
2066+
2067+ return 0;
2068+}
2069+
2070+/* Given a local record, construct a PilotRecord suitable for
2071+ transmission to a Pilot */
2072+PilotRecord * Transmit(SyncAbs* thisSA, LocalRecord* entry)
2073+{
2074+ static PilotRecord p;
2075+ struct Address ai;
2076+ struct AddressAppInfo *aai = NULL;
2077+ char *s;
2078+
2079+ assert(thisSA);
2080+ aai = thisSA->aai;
2081+ assert(aai);
2082+
2083+ ai.entry[entryLastname] = EntryGetAttribute(entry, "sn");
2084+ ai.entry[entryFirstname] = EntryGetAttribute(entry, "givenname");
2085+
2086+ ai.entry[entryCompany] = EntryGetAttribute(entry, "o");
2087+ ai.entry[entryAddress] = EntryGetAttribute(entry, "streetaddress");
2088+ ai.entry[entryCity] = EntryGetAttribute(entry, "locality");
2089+ ai.entry[entryState] = EntryGetAttribute(entry, "st");
2090+ ai.entry[entryZip] = EntryGetAttribute(entry, "postalcode");
2091+ ai.entry[entryCountry] = EntryGetAttribute(entry, "countryname");
2092+ ai.entry[entryTitle] = EntryGetAttribute(entry, "title");
2093+ ai.entry[entryNote] = EntryGetAttribute(entry, "description");
2094+ ai.entry[entryCustom1] = EntryGetAttribute(entry, "xpilot-custom1");
2095+ ai.entry[entryCustom2] = EntryGetAttribute(entry, "xpilot-custom2");
2096+ ai.entry[entryCustom3] = EntryGetAttribute(entry, "xpilot-custom3");
2097+ ai.entry[entryCustom4] = EntryGetAttribute(entry, "xpilot-custom4");
2098+ ai.entry[entryPhone1] = EntryGetAttribute(entry, "xpilot-phone1");
2099+ if (ai.entry[entryPhone1]) {
2100+ d2printf("ai.entry[entryPhone1]=%s, ai.entry[entryPhone1][strlen(ai.entry[entryPhone1])-1]=%c (%d)\n",
2101+ ai.entry[entryPhone1],
2102+ ai.entry[entryPhone1][strlen(ai.entry[entryPhone1])-1],
2103+ ai.entry[entryPhone1][strlen(ai.entry[entryPhone1])-1]);
2104+ }
2105+ ai.entry[entryPhone2] = EntryGetAttribute(entry, "xpilot-phone2");
2106+ ai.entry[entryPhone3] = EntryGetAttribute(entry, "xpilot-phone3");
2107+ ai.entry[entryPhone4] = EntryGetAttribute(entry, "xpilot-phone4");
2108+ ai.entry[entryPhone5] = EntryGetAttribute(entry, "xpilot-phone5");
2109+ ai.phoneLabel[0]
2110+ = PilotFindLabel(aai, EntryGetAttribute(entry, "xpilot-phonelabel1"));
2111+ ai.phoneLabel[1]
2112+ = PilotFindLabel(aai, EntryGetAttribute(entry, "xpilot-phonelabel2"));
2113+ ai.phoneLabel[2]
2114+ = PilotFindLabel(aai, EntryGetAttribute(entry, "xpilot-phonelabel3"));
2115+ ai.phoneLabel[3]
2116+ = PilotFindLabel(aai, EntryGetAttribute(entry, "xpilot-phonelabel4"));
2117+ ai.phoneLabel[4]
2118+ = PilotFindLabel(aai, EntryGetAttribute(entry, "xpilot-phonelabel5"));
2119+ s = EntryGetAttribute(entry, "xpilot-showphone");
2120+ if (s) {
2121+ ai.showPhone = atoi(s);
2122+ } else {
2123+ ai.showPhone = 0;
2124+ }
2125+
2126+ {
2127+ /* If it is a list, use the cn as the last name and put the list
2128+ members in a note with a message indicating it should be edited
2129+ on the Desktop */
2130+ int a = 0;
2131+ int isList = 0;
2132+
2133+ while (EntryFindAttribute(&a, entry, "objectclass") >= 0) {
2134+ if (strcmp(entry->attrs[a++]->value, "groupOfNames") == 0) {
2135+ isList = 1;
2136+ break;
2137+ }
2138+ }
2139+ if (isList) {
2140+ char *note = NULL;
2141+ StringAppend(&note, "DO NOT EDIT ON THE PILOT!\n");
2142+ if (ai.entry[entryNote]) StringAppend(&note, ai.entry[entryNote]);
2143+ StringAppend(&note, "\n");
2144+ ai.entry[entryLastname] = EntryGetAttribute(entry, "cn");
2145+ a = 0;
2146+ while (EntryFindAttribute(&a, entry, "member") >= 0) {
2147+ if (entry->attrs[a]->value) {
2148+ StringAppend(&note, entry->attrs[a]->value);
2149+ StringAppend(&note, "\n");
2150+ }
2151+ a++;
2152+ }
2153+ ai.entry[entryNote] = note;
2154+ }
2155+ }
2156+
2157+ p.length = pack_Address(&ai, NULL, 0);
2158+ p.record = (unsigned char*)calloc(1, p.length);
2159+ pack_Address(&ai, p.record, p.length);
2160+
2161+ {
2162+ char *catID = EntryGetAttribute(entry, "xpilot-categoryID");
2163+ p.category = catID ? atoi(catID) : 0;
2164+ }
2165+ p.attr = RecordNothing;
2166+
2167+ p.archived = entry->archived;
2168+ p.secret = entry->secret;
2169+
2170+ return &p;
2171+}
2172+
2173+/* Free PilotRecord created by Transmit */
2174+int FreeTransmit(SyncAbs* thisSA, LocalRecord* Local, PilotRecord* Remote)
2175+{
2176+ free(Remote->record);
2177+ return 0;
2178+}
2179+
2180+static int strcmpnull(char *a, char *b)
2181+{
2182+ if ((a == NULL || *a == 0) && (b == NULL || *b == 0)) return 0;
2183+ if (a == NULL) return -1;
2184+ if (b == NULL) return 1;
2185+ return strcmp(a, b);
2186+}
2187+
2188+/* Compare a local record and pilot record for inequality */
2189+int Compare(SyncAbs * thisSA, LocalRecord *entry, PilotRecord* p)
2190+{
2191+ struct Address ai;
2192+ Database **db_p = NULL;
2193+ struct AddressAppInfo *aai = NULL;
2194+ int retval = 0;
2195+
2196+ assert(thisSA);
2197+ db_p = thisSA->db_p;
2198+ aai = thisSA->aai;
2199+ assert(aai);
2200+
2201+ unpack_Address(&ai, p->record, p->length);
2202+ if (strcmpnull(EntryGetAttribute(entry, "sn"),
2203+ ai.entry[entryLastname]) !=0) {
2204+ retval = -1;
2205+ } else if (strcmpnull(EntryGetAttribute(entry, "givenname"),
2206+ ai.entry[entryFirstname]) !=0) {
2207+ retval = -1;
2208+ } else if (strcmpnull(EntryGetAttribute(entry, "o"),
2209+ ai.entry[entryCompany]) != 0) {
2210+ retval = -1;
2211+ } else if (strcmpnull(EntryGetAttribute(entry, "streetaddress"),
2212+ ai.entry[entryAddress]) != 0) {
2213+ retval = -1;
2214+ } else if (strcmpnull(EntryGetAttribute(entry, "locality"),
2215+ ai.entry[entryCity]) != 0) {
2216+ retval = -1;
2217+ } else if (strcmpnull(EntryGetAttribute(entry, "st"),
2218+ ai.entry[entryState]) != 0) {
2219+ retval = -1;
2220+ } else if (strcmpnull(EntryGetAttribute(entry, "postalcode"),
2221+ ai.entry[entryZip]) != 0) {
2222+ retval = -1;
2223+ } else if (strcmpnull(EntryGetAttribute(entry, "countryname"),
2224+ ai.entry[entryCountry]) != 0) {
2225+ retval = -1;
2226+ } else if (strcmpnull(EntryGetAttribute(entry, "title"),
2227+ ai.entry[entryTitle]) != 0) {
2228+ retval = -1;
2229+ } else if (strcmpnull(EntryGetAttribute(entry, "description"),
2230+ ai.entry[entryNote]) != 0) {
2231+ retval = -1;
2232+ } else if (strcmpnull(EntryGetAttribute(entry, "telephonenumber"),
2233+ PilotGetPhone(aai, &ai, "Work")) != 0) {
2234+ retval = -1;
2235+ } else if (strcmpnull(EntryGetAttribute(entry, "homephone"),
2236+ PilotGetPhone(aai, &ai, "Home")) != 0) {
2237+ retval = -1;
2238+ } else if (strcmpnull(EntryGetAttribute(entry, "facsimiletelephonenumber"),
2239+ PilotGetPhone(aai, &ai, "Fax")) != 0) {
2240+ retval = -1;
2241+ } else if (strcmpnull(EntryGetAttribute(entry, "pagerphone"),
2242+ PilotGetPhone(aai, &ai, "Pager")) != 0) {
2243+ retval = -1;
2244+ } else if (strcmpnull(EntryGetAttribute(entry, "cellphone"),
2245+ PilotGetPhone(aai, &ai, "Mobile")) != 0) {
2246+ retval = -1;
2247+ } else if (strcmpnull(EntryGetAttribute(entry, "mail"),
2248+ PilotGetPhone(aai, &ai, "E-mail")) != 0) {
2249+ retval = -1;
2250+ }
2251+ if (retval == -1) {
2252+ static int reported = 0;
2253+
2254+ if (!reported) {
2255+ dlp_AddSyncLogEntry(thisSA->sd, "At least one record was modified/deleted on one platform and modified/deleted on the other in a different way. The modified versions will appear on both platforms, possibly resulting in an unwanted record on both platforms. Delete the unwanted records on one platform and HotSync again.\n");
2256+ reported = 0;
2257+ }
2258+ }
2259+
2260+ return retval;
2261+}
2262+
2263+/* Find a local backup record and compare it to the pilot record for
2264+ inequality */
2265+int CompareBackup(SyncAbs * thisSA, LocalRecord* entry, PilotRecord* p)
2266+{
2267+ /* TBD: Is this function ever actually used? */
2268+ return Compare(thisSA, entry, p);
2269+}
2270+
2271+/* Delete all local records */
2272+int DeleteAll(SyncAbs * thisSA)
2273+{
2274+ Database *db = NULL;
2275+
2276+ assert(thisSA);
2277+ if (!thisSA->db_p) return 0;
2278+ db = *(thisSA->db_p);
2279+ while (db->num_entries)
2280+ DatabaseDeleteEntry(db, 0);
2281+ return 0;
2282+}
2283+
2284+/* Do a local purge, deleting all records marked RecordDeleted, and
2285+ archiving all records marked for archiving */
2286+int Purge(SyncAbs * thisSA)
2287+{
2288+ Database *db = NULL;
2289+ int e = 0;
2290+
2291+ assert(thisSA);
2292+ if (!thisSA->db_p) return 0;
2293+ db = *(thisSA->db_p);
2294+ e = 0;
2295+ while (e < db->num_entries) {
2296+ char *dn = NULL;
2297+ assert(db->entries);
2298+ assert(db->entries[e]);
2299+
2300+ dn = EntryGetAttribute(db->entries[e], "dn");
2301+ if (db->entries[e]->archived) {
2302+ dprintf("Archiving the following record on the PC:\n\t");
2303+ dprintf("%s\n\n", dn?dn:"No dn attribute");
2304+ DatabaseAppendEntry(thisSA->db_archive_p, EntryCopy(db->entries[e]));
2305+ }
2306+ if (db->entries[e]->attr == RecordDeleted) {
2307+ dprintf("Deleting the following record on the PC:\n\t");
2308+ dprintf("%s\n\n", dn?dn:"No dn attribute");
2309+
2310+ DatabaseDeleteEntry(db, e);
2311+ } else {
2312+ e++;
2313+ }
2314+ }
2315+ return 0;
2316+}
2317+
2318+/* Add remote record to archive. l is non-NULL if there is a matching local record */
2319+int ArchiveRemote(SyncAbs * s, LocalRecord * l, PilotRecord * p) {
2320+ /* Not necessary. Remote records marked for archival all also
2321+ marked as modified, so StoreRemote stores them in the local
2322+ database. Purge moves them to the archive database. */
2323+ return 0;
2324+}
2325+
2326+int ClearStatusArchiveLocal(SyncAbs *thisSA, LocalRecord *entry)
2327+{
2328+ /* TODO */
2329+ return 0;
2330+}
2331+
2332+int ArchiveLocal(SyncAbs *thisSA, LocalRecord *entry)
2333+{
2334+ /* TODO */
2335+ return 0;
2336+}
2337+
2338+void SyncWithPilot(Database **db_sync_p, Database **db_archive_p,
2339+ int slow_sync)
2340+{
2341+ struct pi_sockaddr addr;
2342+ int db;
2343+ int sd;
2344+ struct PilotUser U;
2345+ int ret;
2346+ struct SyncAbs abs;
2347+ int quiet = 0;
2348+
2349+ if (getenv("PILOTPORT")) {
2350+ strcpy(addr.pi_device,getenv("PILOTPORT"));
2351+ } else {
2352+ strcpy(addr.pi_device,PILOTPORT);
2353+ }
2354+ addr.pi_family = PI_AF_SLP;
2355+
2356+ /* Set up abstraction structure */
2357+ abs.MatchRecord = MatchRecord;
2358+ abs.FreeMatch = FreeMatch;
2359+ abs.Iterate = Iterate;
2360+ abs.IterateSpecific = IterateSpecific;
2361+ abs.SetStatus = SetStatus;
2362+ abs.SetArchived = SetArchived;
2363+ abs.SetPilotID = SetPilotID;
2364+ abs.GetPilotID = GetPilotID;
2365+ abs.StoreRemote = StoreRemote;
2366+ abs.ArchiveLocal = ArchiveLocal;
2367+ abs.ClearStatusArchiveLocal = ClearStatusArchiveLocal;
2368+ abs.ArchiveRemote = ArchiveRemote;
2369+ abs.DeleteAll = DeleteAll;
2370+ abs.Purge = Purge;
2371+ abs.CompareBackup = CompareBackup;
2372+ abs.Compare = Compare;
2373+ abs.Transmit = Transmit;
2374+ abs.FreeTransmit = FreeTransmit;
2375+
2376+ abs.db_p = db_sync_p;
2377+ abs.db_archive_p = db_archive_p;
2378+
2379+ if (!quiet) {
2380+ fprintf(stderr,
2381+ "Please insert Pilot in cradle on %s and press HotSync button.\n",
2382+ addr.pi_device);
2383+ }
2384+
2385+ if (!(sd = pi_socket(PI_AF_SLP, PI_SOCK_STREAM, PI_PF_PADP))) {
2386+ perror("pi_socket");
2387+ exit(1);
2388+ }
2389+
2390+ ret = pi_bind(sd, (struct sockaddr*)&addr, sizeof(addr));
2391+ if(ret == -1) {
2392+ perror("pi_bind");
2393+ exit(1);
2394+ }
2395+
2396+ ret = pi_listen(sd,1);
2397+ if(ret == -1) {
2398+ perror("pi_listen");
2399+ exit(1);
2400+ }
2401+
2402+ sd = pi_accept(sd, 0, 0);
2403+ if(sd == -1) {
2404+ perror("pi_accept");
2405+ exit(1);
2406+ }
2407+
2408+ /* Ask the pilot who it is. */
2409+ dlp_ReadUserInfo(sd,&U);
2410+
2411+ /* Tell user (via Pilot) that we are starting things up */
2412+ dlp_OpenConduit(sd);
2413+
2414+ /* Open the Address database, store access handle in db */
2415+ if(dlp_OpenDB(sd, 0, dlpOpenRead|dlpOpenWrite, "AddressDB", &db) < 0) {
2416+ puts("Unable to open AddressDB");
2417+ dlp_AddSyncLogEntry(sd, "Unable to open AddressDB.\n");
2418+ exit(1);
2419+ }
2420+
2421+ abs.sd = sd;
2422+
2423+ /* Get the AddressApplicationInfo */
2424+ abs.aai = (struct AddressAppInfo *)malloc(sizeof(struct AddressAppInfo));
2425+
2426+ {
2427+ int l;
2428+ char buf[0xffff];
2429+
2430+ l = dlp_ReadAppBlock(sd, db, 0, (unsigned char *)buf, 0xffff);
2431+ unpack_AddressAppInfo(abs.aai, (unsigned char *)buf, l);
2432+ }
2433+
2434+ abs.slow_sync = slow_sync;
2435+ if (slow_sync) {
2436+ SlowSync(sd, db, &abs);
2437+ } else {
2438+ FastSync(sd, db, &abs);
2439+ }
2440+
2441+
2442+ free(abs.aai);
2443+
2444+ /* Close the database */
2445+ dlp_CloseDB(sd, db);
2446+
2447+ /* Tell the user who it is, with a different PC id. */
2448+ /* TBD: What is this? */
2449+ U.lastSyncPC = 0xDEADBEEF;
2450+ U.successfulSyncDate = time(NULL);
2451+ U.lastSyncDate = U.successfulSyncDate;
2452+ dlp_WriteUserInfo(sd,&U);
2453+
2454+ dlp_AddSyncLogEntry(sd, "Wrote addresses to Pilot.\n");
2455+
2456+ /* All of the following code is now unnecessary, but harmless */
2457+
2458+ dlp_EndOfSync(sd,0);
2459+ pi_close(sd);
2460+}
2461+
2462+void EntrySetPhone(Entry **entry_p, char *phoneLabel, char *value)
2463+{
2464+ int i = 1;
2465+ int found = 0;
2466+ int firstUnused = 0;
2467+ char *label = NULL;
2468+ char *number = NULL;
2469+ char buf[40];
2470+
2471+ for (i = 1; entry_p && i <= 5 && !found; i++) {
2472+
2473+ sprintf(buf, "xpilot-phonelabel%d", i);
2474+ label = EntryGetAttribute(*entry_p, buf);
2475+ if (label && strcmp(label, phoneLabel) == 0) {
2476+ found = 1;
2477+ break;
2478+ }
2479+ sprintf(buf, "xpilot-phone%d", i);
2480+ number = EntryGetAttribute(*entry_p, buf);
2481+ if (firstUnused == 0 && (!number || number[0] == '\0')) {
2482+ firstUnused = i;
2483+ }
2484+ }
2485+ if (found) {
2486+ sprintf(buf, "xpilot-phone%d", i);
2487+ EntrySetAttribute(entry_p, buf, value);
2488+ } else if (value && value[0]) {
2489+ if (firstUnused) {
2490+ sprintf(buf, "xpilot-phonelabel%d", firstUnused);
2491+ EntrySetAttribute(entry_p, buf, phoneLabel);
2492+ sprintf(buf, "xpilot-phone%d", firstUnused);
2493+ EntrySetAttribute(entry_p, buf, value);
2494+ } else {
2495+ fprintf(stderr,
2496+ "No unused phone slots on pilot to store %s in.\n", value);
2497+ }
2498+ }
2499+}
2500+
2501+/* Are changes in this attribute worth marking the record as changed? */
2502+int PropagationAttributeQ(Attribute *attr)
2503+{
2504+ const int num_prop_types = 19;
2505+ char *prop_types[] = {
2506+ "cn",
2507+ "mail",
2508+ "o",
2509+ "locality",
2510+ "givenname",
2511+ "sn",
2512+ "st",
2513+ "description",
2514+ "title",
2515+ "streetaddress",
2516+ "postalcode",
2517+ "countryname",
2518+ "telephonenumber",
2519+ "homephone",
2520+ "facsimiletelephonenumber",
2521+ "ou",
2522+ "pagerphone",
2523+ "cellphone",
2524+ "xmozillaanyphone",
2525+ };
2526+ int t = 0;
2527+
2528+ assert(attr);
2529+ for (t = 0; t < num_prop_types; t++) {
2530+ if (strcmp(prop_types[t], attr->type) == 0) return 1;
2531+ }
2532+ return 0;
2533+}
2534+
2535+void MergeToDb(Database **db_sync_p, Database *db)
2536+{
2537+ int e, a;
2538+ const int num_netscape_types = 26;
2539+ char *netscape_types[] = {
2540+ "dn",
2541+ "modifytimestamp",
2542+ "cn",
2543+ "xmozillanickname",
2544+ "mail",
2545+ "xmozillausehtmlmail",
2546+ "o",
2547+ "locality",
2548+ "givenname",
2549+ "sn",
2550+ "st",
2551+ "description",
2552+ "title",
2553+ "streetaddress",
2554+ "postalcode",
2555+ "countryname",
2556+ "telephonenumber",
2557+ "homephone",
2558+ "facsimiletelephonenumber",
2559+ "xmozillauseconferenceserver",
2560+ "ou",
2561+ "pagerphone",
2562+ "cellphone",
2563+ "homeurl",
2564+ "xmozillaanyphone",
2565+ "objectclass",
2566+ };
2567+
2568+ assert(db_sync_p != NULL && db != NULL);
2569+
2570+ /* Mark all sync entries as deleted initially */
2571+ if (db_sync_p) {
2572+ Database *dbs = *db_sync_p;
2573+
2574+ for (e = 0; dbs && e < dbs->num_entries; e++) {
2575+ Entry *entry = dbs->entries[e];
2576+ entry->attr = RecordDeleted;
2577+ }
2578+ }
2579+
2580+
2581+ for (e = 0; e < db->num_entries; e++) {
2582+ Entry *entry = db->entries[e];
2583+ Entry *sync_entry = NULL;
2584+
2585+ /* Find a matching entry */
2586+ if (*db_sync_p) {
2587+ sync_entry = DatabaseFindEntry(*db_sync_p, entry);
2588+ }
2589+
2590+ if (!sync_entry) {
2591+ /* If none found, make a new one */
2592+ sync_entry = EntryCopy(entry);
2593+ d2printf("The following entry is new on the PC:\n");
2594+ d2EntryPrint(sync_entry, stdout);
2595+ d2printf("\n");
2596+ DatabaseAppendEntry(db_sync_p, sync_entry);
2597+ sync_entry->attr = RecordNew;
2598+ } else {
2599+ d2printf("The following entry already exists on the PC:\n");
2600+ d2EntryPrint(sync_entry, stdout);
2601+ d2printf("\n");
2602+ /* Assume the entry is unchanged unless proven otherwise */
2603+ sync_entry->attr = RecordNothing;
2604+
2605+ /* Look for attributes that we use which are new, modified, or
2606+ deleted and propogate them to the sync database. */
2607+
2608+ /* Mark all of the attributes that we use as deleted initially. */
2609+ for (a = 0; a < num_netscape_types; a++) {
2610+ int sa = 0;
2611+
2612+ while (EntryFindAttribute(&sa, sync_entry,
2613+ netscape_types[a]) >= 0) {
2614+ sync_entry->attrs[sa++]->status = RecordDeleted;
2615+ }
2616+ }
2617+
2618+ /* See if any attributes are new or have been changed */
2619+ for (a = 0; a < entry->num_attrs; a++) {
2620+ Attribute *attr = entry->attrs[a];
2621+ Attribute *sync_attr = NULL;
2622+ int sa = 0;
2623+ int t = 0;
2624+
2625+ /* If this isn't one of the attributes we use, just skip it */
2626+ for (t = 0; t < num_netscape_types; t++) {
2627+ if (strcmp(attr->type, netscape_types[t]) == 0) break;
2628+ }
2629+ if (t >= num_netscape_types) {
2630+ continue;
2631+ }
2632+
2633+ /* Find the first matching attribute still marked RecordDeleted */
2634+ while (sa < sync_entry->num_attrs
2635+ && EntryFindAttribute(&sa, sync_entry,
2636+ attr->type) >= 0
2637+ && sync_entry->attrs[sa]->status != RecordDeleted) {
2638+ sa++;
2639+ }
2640+
2641+ if (sa >= sync_entry->num_attrs) {
2642+ /* If none found, make a new one */
2643+ sync_attr = AttributeCopy(attr);
2644+ EntryAppendAttribute(&sync_entry, sync_attr);
2645+ if (PropagationAttributeQ(sync_attr)) {
2646+ sync_attr->status = RecordNew;
2647+ d2printf("The following attribute is new on the PC:\n\t");
2648+ d2AttributePrint(sync_attr, stdout);
2649+ sync_entry->attr = RecordModified;
2650+ } else {
2651+ sync_attr->status = RecordNothing;
2652+ }
2653+ } else {
2654+ /* If we found it, compare it */
2655+ sync_attr = sync_entry->attrs[sa];
2656+ if (attr->value_length != sync_attr->value_length
2657+ || memcmp(attr->value, sync_attr->value, attr->value_length)) {
2658+ /* Not the same, so overwrite with the new value */
2659+ if (PropagationAttributeQ(sync_attr)) {
2660+ d2printf("The following attribute on the PC changed from:\n\t");
2661+ d2AttributePrint(sync_attr, stdout);
2662+ d2printf("To:\n\t");
2663+ d2AttributePrint(attr, stdout);
2664+ }
2665+
2666+ free(sync_attr);
2667+ sync_attr = AttributeCopy(attr);
2668+ sync_entry->attrs[sa] = sync_attr;
2669+
2670+ if (PropagationAttributeQ(sync_attr)) {
2671+ sync_attr->status = RecordModified;
2672+ sync_entry->attr = RecordModified;
2673+ } else {
2674+ sync_attr->status = RecordNothing;
2675+ }
2676+ } else {
2677+ /* It is the same so mark it unchanged */
2678+ sync_attr->status = RecordNothing;
2679+ }
2680+ }
2681+ } /* for "a" loop */
2682+
2683+ /* If any of the attributes that we use were not matched, then they
2684+ should be deleted and the record should be marked as modified */
2685+ for (a = 0; a < num_netscape_types; a++) {
2686+ int sa = 0;
2687+
2688+ while (EntryFindAttribute(&sa, sync_entry,
2689+ netscape_types[a]) >= 0) {
2690+ if (sync_entry->attrs[sa]->status == RecordDeleted) {
2691+ d2printf("The following attribute was deleted on the PC:\n\t");
2692+ d2AttributePrint(sync_entry->attrs[sa], stdout);
2693+ EntryDeleteAttribute(sync_entry, sa);
2694+ sync_entry->attr = RecordModified;
2695+ } else {
2696+ sa++;
2697+ } /* if */
2698+ } /* while */
2699+ } /* for */
2700+
2701+ } /* if matching entry */
2702+
2703+ /* For each phone type, look for a corresponding pilot field.
2704+ Replace the contents of the first corresponding field. If
2705+ there is no corresponding field, look for an empty/unused field
2706+ and set its label and contents. If there are no empty/unused
2707+ fields, there is no place to put the info on the pilot so give
2708+ up (with possible warning message). */
2709+ assert(sync_entry);
2710+ EntrySetPhone(&sync_entry, "Work",
2711+ EntryGetAttribute(sync_entry, "telephonenumber"));
2712+ EntrySetPhone(&sync_entry, "Home",
2713+ EntryGetAttribute(sync_entry, "homephone"));
2714+ EntrySetPhone(&sync_entry, "Fax",
2715+ EntryGetAttribute(sync_entry, "facsimiletelephonenumber"));
2716+ EntrySetPhone(&sync_entry, "Pager",
2717+ EntryGetAttribute(sync_entry, "pagerphone"));
2718+ EntrySetPhone(&sync_entry, "Mobile",
2719+ EntryGetAttribute(sync_entry, "cellphone"));
2720+ EntrySetPhone(&sync_entry, "E-mail",
2721+ EntryGetAttribute(sync_entry, "mail"));
2722+ } /* for each entry */
2723+
2724+ /* Notify user of deleted entries */
2725+ if (db_sync_p) {
2726+ Database *dbs = *db_sync_p;
2727+
2728+ for (e = 0; dbs && e < dbs->num_entries; e++) {
2729+ Entry *entry = dbs->entries[e];
2730+ if (entry->attr == RecordDeleted) {
2731+ d2printf("The following entry was deleted on the PC:\n");
2732+ d2EntryPrint(entry, stdout);
2733+ d2printf("\n");
2734+ }
2735+ }
2736+ }
2737+
2738+}
2739+
2740+
2741+int main(int argc, char *argv[])
2742+{
2743+ char *ldif_file = NULL;
2744+ char *ldif_sync_file = NULL;
2745+ char *ldif_archive_file = NULL;
2746+ char *ldif_archive_default = "archive.ldif";
2747+ char *ldif_sync_default = "sync.ldif";
2748+ Database *db = NULL;
2749+ Database *db_sync = NULL;
2750+ Database *db_archive = NULL;
2751+ int slow_sync = 0;
2752+ char *usage =
2753+#include "sync-ldif.usage.h"
2754+ ;
2755+ char *home = getenv("HOME");
2756+ int arg = 1;
2757+
2758+ if (arg < argc && strcmp(argv[arg], "-d") == 0) {
2759+ sync_ldif_debug = 1;
2760+ arg++;
2761+ }
2762+
2763+ if (argc - arg < 1 || argc - arg > 3) {
2764+ fprintf(stderr, usage);
2765+ exit(1);
2766+ }
2767+
2768+ ldif_file = argv[arg];
2769+ db = ReadLdif(ldif_file);
2770+ if (!db) {
2771+ fprintf(stderr, "Failed to open %s for reading\n", ldif_file);
2772+ exit(1);
2773+ }
2774+ arg++;
2775+
2776+ if (arg < argc) {
2777+ StringAppend(&ldif_sync_file, argv[arg]);
2778+ arg++;
2779+ } else {
2780+ if (home) {
2781+ StringAppend(&ldif_sync_file, home);
2782+ StringAppend(&ldif_sync_file, "/");
2783+ }
2784+ StringAppend(&ldif_sync_file, ".sync-ldif");
2785+ mkdir(ldif_sync_file, 0755);
2786+ StringAppend(&ldif_sync_file, "/");
2787+ StringAppend(&ldif_sync_file, ldif_sync_default);
2788+ }
2789+
2790+ if (arg < argc) {
2791+ StringAppend(&ldif_archive_file, argv[arg]);
2792+ arg++;
2793+ } else {
2794+ if (home) {
2795+ StringAppend(&ldif_archive_file, home);
2796+ StringAppend(&ldif_archive_file, "/");
2797+ }
2798+ StringAppend(&ldif_archive_file, ".sync-ldif");
2799+ mkdir(ldif_archive_file, 0755);
2800+ StringAppend(&ldif_archive_file, "/");
2801+ StringAppend(&ldif_archive_file, ldif_archive_default);
2802+ }
2803+
2804+ /* Read the sync database or create a new one and do a slow sync
2805+ if one doesn't exist */
2806+ db_sync = ReadLdif(ldif_sync_file);
2807+ if (!db_sync) {
2808+ fprintf(stderr, "Warning: Synchronization file (%s) not found. Performing a slow sync to create an initial one.\n", ldif_sync_file);
2809+ db_sync = DatabaseCreate();
2810+ slow_sync = 1;
2811+ }
2812+
2813+ MergeToDb(&db_sync, db);
2814+
2815+ /* Read the archive database or create a new one if one doesn't exist */
2816+ db_archive = ReadLdif(ldif_archive_file);
2817+ if (!db_archive) {
2818+ fprintf(stderr, "Creating an archive file (%s)\n", ldif_archive_file);
2819+ db_archive = DatabaseCreate();
2820+ }
2821+
2822+ SyncWithPilot(&db_sync, &db_archive, slow_sync);
2823+ WriteLdif(db_sync, ldif_sync_file);
2824+ WriteLdif(db_archive, ldif_archive_file);
2825+ WriteLdif(db_sync, ldif_file);
2826+ return 0;
2827+}
2828diff -Nru pilot-link.0.9.3/sync-ldif.usage.h pilot-link.0.9.3.new/sync-ldif.usage.h
2829--- pilot-link.0.9.3/sync-ldif.usage.h Thu Jan 1 01:00:00 1970
2830+++ pilot-link.0.9.3.new/sync-ldif.usage.h Fri May 28 15:54:22 1999
2831@@ -0,0 +1,88 @@
2832+
2833+"\n"
2834+"\n"
2835+"Usage: sync-ldif [-v] <ldif_file> [<ldif_sync_file> [<ldif_archive_file>]]\n"
2836+"\n"
2837+"sync-ldif attempts to synchronize the PalmPilot address book with a\n"
2838+"Netscape Communicator address book LDIF file.\n"
2839+"\n"
2840+"To use it:\n"
2841+"\n"
2842+"0. BE SAFE AND BACKUP YOUR PALMPILOT AND YOUR COMMUNICATOR ADDRESS\n"
2843+"BOOK. To backup you Communicator address book, create a new address\n"
2844+"book, select all of your old address book entries (i.e. cards) and\n"
2845+"copy them into the new address book.\n"
2846+"\n"
2847+"1. Select 'File/Export' in your Communicator address book and enter a\n"
2848+"file name such as 'net.ldif'. This is the <ldif_file>.\n"
2849+"\n"
2850+"2. Run 'sync-ldif -v <ldif_file>'. It should read <ldif_file>,\n"
2851+"synchronize with the PalmPilot, and then replace <ldif_file> with a\n"
2852+"synchronized version suitable for importing into Communicator. It\n"
2853+"also reads and writes two other files. See below for details. The\n"
2854+"'-v' option causes 'sync-ldif' to be more verbose and print a short\n"
2855+"message each time it makes a modification to the PalmPilot or\n"
2856+"<ldif_file>.\n"
2857+"\n"
2858+"3. Delete all of the entries in your Communicator address book. The\n"
2859+"menu items 'Edit/Select All' and 'Edit/Delete' should do the trick.\n"
2860+"You made a backup right?\n"
2861+"\n"
2862+"4. Select 'File/Import' in your Communicator address book and enter the\n"
2863+"name of the <ldif_file> you used in step 1.\n"
2864+"\n"
2865+"With a little luck, both Communicator and your PalmPilot should now\n"
2866+"contain entries from both.\n"
2867+"\n"
2868+"The first time you run the program, it will do a slow sync and create\n"
2869+"a directory in your home directory called '.sync-ldif/'. Here it will\n"
2870+"create and maintain the default <ldif_sync_file> 'sync.ldif' and the\n"
2871+"default <ldif_archive_file> 'archive.ldif'. If you feel daring, you\n"
2872+"can override either of these two locations when you run the program.\n"
2873+"This might be necessary if you have multiple Communicator address\n"
2874+"books, or multiple PalmPilots.\n"
2875+"\n"
2876+"<ldif_sync_file> contains the <ldif_file> that resulted from the last\n"
2877+"run of the program. It is used to determine what changed in\n"
2878+"Communicator between syncs. WARNING #1: If you synchronize your\n"
2879+"PalmPilot address book with something other than <ldif_sync_file>\n"
2880+"(e.g. another computer, application, or file), you should remove\n"
2881+"<ldif_sync_file> to force a slow sync. WARNING #2: Do not synchronize\n"
2882+"multiple PalmPilots with the same <ldif_sync_file>.\n"
2883+"\n"
2884+"<ldif_archive_file> contains all entries that you marked for archival\n"
2885+"on the PalmPilot. The program appends newly archived entries each\n"
2886+"time it runs. To recover entries from the archive file, use\n"
2887+"Communicator to import <ldif_archive_file> into a new address book and\n"
2888+"then copy the desired entries to your main address book.\n"
2889+"\n"
2890+"If either your PalmPilot or your Communicator address book becomes\n"
2891+"corrupted, restore it from backup, remove <ldif_sync_file> to force a\n"
2892+"slow sync, and run sync-ldif again.\n"
2893+"\n"
2894+"FAQ: What is with the entries which start with '!' within Communicator?\n"
2895+"\n"
2896+"Short answer: They indicate a potential conflict. Look for another\n"
2897+"entry with the same name or email. If you find one, do a manual merge\n"
2898+"if necessary and then delete one. If you don't find a matching\n"
2899+"entry, do not worry about it. One way or the other, remove the '!'s and\n"
2900+"everything should be OK.\n"
2901+"\n"
2902+"Long answer: If you (a) add an entry on the PalmPilot, (b) modify an\n"
2903+"existing entry in different ways in Communicator and the PalmPilot, or\n"
2904+"(c) modify an entry on the PalmPilot such that it has the same name or\n"
2905+"email as an entry in Communicator which currently exists or has been\n"
2906+"deleted since the last sync, the sync algorithm tries to create a new\n"
2907+"entry in Communicator. This new entry must have a unique name and\n"
2908+"email. If the name or email you want already exists in Communicator,\n"
2909+"the sync algorithm prepends '!'s to both the email and the name until\n"
2910+"they are both unique in Communicator.\n"
2911+"\n"
2912+"PalmPilot is a registered trademark of 3Com Corporation or its\n"
2913+"subsidiaries.\n"
2914+"\n"
2915+"Netscape and Netscape Communicator are registered trademarks of Netscape\n"
2916+"Communications Corporation in the United States and other countries.\n"
2917+"\n"
2918+
2919+"\n"
This page took 0.462963 seconds and 4 git commands to generate.