SyncBBDB-2.6/SyncBBDB/localBBDB.pm0000644000076400007640000002606110425462100015604 0ustar kaplankaplan#------------------------------------------------------------------------------ # $Date: 2006/05/01 19:40:48 $ # RCS: $Id: localBBDB.pm,v 1.3 2006/05/01 19:40:48 aaronkaplan Exp $ #------------------------------------------------------------------------------ package localBBDB; use strict; use POSIX qw(strftime); use bbdb::bbdb; use bbdb::bbdbRecord; use SyncUtil::localHashDB; use SyncBBDB::pilotBBDBMap; @localBBDB::ISA = qw(localHashDB); ## Phone number stuff... my $parea = '1?\s*[-.\(]?\s*([2-9][0-9][0-9])[-.\)\s]+'; my $pmain = '([1-9][0-9][0-9])[-.\s]*([0-9][0-9][0-9][0-9])'; my $pext = '[-.x\s]+(\d+)'; my $phoneRE1 = "^\\s*" . $parea . $pmain . $pext . "\\s*\$"; my $phoneRE2 = "^\\s*" . $parea . $pmain . "\\s*\$"; my $phoneRE3 = "^\\s*" . $pmain . $pext . "\\s*\$"; my $phoneRE4 = "^\\s*" . $pmain . "\\s*\$"; my $phoneRE5 = "^[-0-9 EeXxtT.()+]*\$"; sub new { my $type = shift; my $self = localHashDB->new(shift, shift); bless ($self, $type); $self->{'bbdbFile'} = shift; $self->{'translate'} = shift; $self->{'no-hash-fields'} = ['timestamp']; return $self; } sub dupRec { my $self = shift; my $lrec = shift; my $prec = shift; my $ret = new bbdbRecord($lrec->ePStr()); $ret->{'notes'}{'creation-date'} = strftime("%Y-%m-%d", localtime); delete $ret->{'notes'}{'pilot-id'}; ## disassociate from any pilot recs push(@{$self->{'BBDB'}->{'records'}}, $ret); push(@{$self->{'BBDB'}->{'sync recs'}},$ret); return $ret; } sub finish { my $self = shift; $self->localHashDB::finish(); my $file = new FileHandle(">$self->{'bbdbFile'}"); $self->{'BBDB'}->eprint($file); undef $file; } sub name { my $self = shift; return "Emacs BBDB"; } sub setup { my $self = shift; # This should give us a rough idea of the number of records; my $nids = $self->{'hashObj'}->getNumIds(); $nids = 100 if ($nids == 0); # Make sure this isn't 0 $self->{'in bbdb read'} = $nids; $self->{'in bbdb count'} = 0; $self->{'BBDB'} = new bbdb($self->{'bbdbFile'}, $self); if ($self->{'in bbdb count'} eq 0) { if (! -f $self->{'bbdbFile'}) { ### FIXX: should probably ask the user if he wants to proceed... $self->getHost()->output("BBDB file not found: ". $self->{'bbdbFile'}."\n"); } else { ### FIXX: should probably ask the user if he wants to proceed... $self->getHost()->output("BBDB file was found but is empty: ". $self->{'bbdbFile'}."\n"); } } delete $self->{'in bbdb read'}; delete $self->{'in bbdb count'}; $self->localHashDB::setup(shift, shift); } sub getRecords { my $self = shift; if (!defined $self->{'sync recs'}) { $self->{'sync recs'} = []; foreach my $rec (@{$self->{'BBDB'}->{'records'}}) { # Skip adding the record if I'm supposed to ignore it. push(@{$self->{'sync recs'}}, $rec) unless (defined $rec->{'notes'}{'pilot-ignore'}); } } return $self->{'sync recs'}; } sub getPilotIDs { my $self = shift; my $lrec = shift; my $map = new pilotBBDBMap($lrec, $self->{'translate'}); my @ret; foreach my $rec (@{$map->{'records'}}) { push(@ret, $rec->{'id'}); } return \@ret; } sub deleteRec { my $self = shift; my $id = shift; my $rec = $self->{'hash'}{$id}; return if (not defined $rec); # clear out the hash map entries... $self->{'hashObj'}->setLocalHash($id, undef); $self->{'hashObj'}->setPilotHash($id, undef); my $map = new pilotBBDBMap($rec, $self->{'translate'}); my $mrec = $map->delMapForID($id); if (scalar(@{$map->{'records'}})) { # Other mappings still exist so don't delete the record. $rec->{'notes'}{'pilot-id'} = $map->toString(); return $rec; } # Delete the record from the list to be synced. foreach my $r (@{$self->getRecords()}) { if ((defined $r) && ($r == $rec)) { $r = undef; last; } } # really delete the record from BBDB. foreach my $r (@{$self->{'BBDB'}->{'records'}}) { if ((defined $r) && ($r == $rec)) { $r = undef; last; } } return $rec; } sub syncRec { my $self = shift; my $prec = shift; my $lrec = shift; my $pilotdb = $self->{'pilotDB'}; my $is_new_rec = 0; if (not defined $lrec) { $lrec = new bbdbRecord(); $is_new_rec = 1; push(@{$self->{'BBDB'}->{'records'}}, $lrec); push(@{$self->{'BBDB'}->{'sync recs'}},$lrec); } my $map = new pilotBBDBMap($lrec, $self->{'translate'}); my $mapRec = $map->getMapForID($prec->{'id'}); if (!defined $mapRec) { # new Pilot Record, need a new mapping record... $mapRec = new pilotBBDBRecMap($prec->{'id'}, $pilotdb->categoryStr($prec), "Main"); push(@{$map->{'records'}}, $mapRec); } $map->updateMappingFromPilot($pilotdb); # The fields that look like they are new at this point, which means # they exist in bbdb but don't have any mappings to the palm or # as 'skipped' fields, are the fields that were deleted on the palm. my %toDel = %{$map->getNewBBDBFields()}; foreach my $f (keys %toDel) { $f =~ m/([^:]+):(.*)/; my $type = $1; my $name = $2; # for net and AKA I just skip deleting them since it is # particularly unclear if the intent is to remove the # mapping for the palm records. if ($type eq "addr") { $lrec->del_addr($name); } elsif ($type eq "phone") { $lrec->del_phone($name); } elsif ($type eq "note" && $name ne 'notes') { delete $lrec->{'notes'}{$name}; } } my $lname = $pilotdb->getField($prec, 'Last'); $lrec->{'lname'} = $lname if ($lname); my $fname = $pilotdb->getField($prec, 'First'); $lrec->{'fname'} = $fname if ($fname); my $org = $pilotdb->getField($prec, 'Company'); $lrec->{'org'} = $org if ($org); my $title = $pilotdb->getField($prec, 'Title'); $lrec->{'notes'}{'title'} = $title if ($title); my $notes = $pilotdb->getField($prec, 'Note'); $lrec->{'notes'}{'notes'} = $notes if ($notes); foreach $mapRec (@{$map->{'records'}}) { $prec = $pilotdb->getRec($mapRec->{'id'}); $self->{'hashObj'}->setLocalHash($mapRec->{'id'}, undef); $mapRec->{'category'} = $pilotdb->categoryStr($prec); my $addr; if (defined $mapRec->{'name'}) { $addr = $lrec->{'addrH'}{$mapRec->{'name'}}; if (not defined $addr) { $addr = new bbdbAddr("nil"); $addr->{'name'} = $mapRec->{'name'}; } } else { $addr = new bbdbAddr("nil"); $addr->{'name'} = "Main"; } my $paddr = $pilotdb->getField($prec, 'Address'); my @streets = split("\xA", $paddr) if ($paddr); $addr->{'street'} = \@streets; $addr->{'city'} = $pilotdb->getField($prec, 'City'); $addr->{'state'} = $pilotdb->getField($prec, 'State'); my $zip = $pilotdb->getField($prec, 'Zip'); my @zips; if ($zip) { if ($self->{'BBDB'}{'version'} ge 6) { # Ok for version 6 and up we can just slam the zip in # we store it as the first element of an array... push(@zips, $zip); } elsif ($zip =~ m/^[-0-9\s]*$/) { # US Zip??? @zips = split(/[-\s]+/,$zip); } else { # intl zip. @zips = split(/[\s]+/,$zip); if (scalar(@zips) < 2) { # BBDB requires at least two fields for string rep of zip. # So we will push on an empty string.... (UHGG!) push(@zips,""); } } } $addr->{'zip'} = \@zips; $addr->{'country'} = $pilotdb->getField($prec, 'Country'); # if we have any data push it back to BBDB if (scalar(@{$addr->{'street'}}) || scalar(@{$addr->{'zip'}}) || $addr->{'city'} || $addr->{'state'} ||$addr->{'country'}) { $lrec->add_addr($addr); $mapRec->{'name'} = $addr->{'name'}; } elsif (defined $mapRec->{'name'}) { # deleted addr case... $lrec->del_addr($mapRec->{'name'}); # Delete the association with the address info. # After all they didn't delete the Pilot record. delete $mapRec->{'name'}; } foreach my $ent (@{$mapRec->{'mappings'}}) { my $btype = $ent->{'bbdb-type'}; my $bname = $ent->{'bbdb-name'}; my $pname = $ent->{'pilot'}; # print STDERR "Type: $btype\n"; my $val = $pilotdb->getField($prec, $pname); if ($btype eq "phone") { ## Phone number. my $phone; if ($val =~ m/$phoneRE1/) { ## (415) 555-1212 x123 $phone = new bbdbPhone("\"$bname\" $1 $2 $3 $4"); } elsif ($val =~ m/$phoneRE2/) { ## (415) 555-1212 $phone = new bbdbPhone("\"$bname\" $1 $2 $3"); } elsif ($val =~ m/$phoneRE3/) { ## 555-1212 x123 $phone = new bbdbPhone("\"$bname\" 0 $1 $2 $3"); } elsif ($val =~ m/$phoneRE4/) { ## 555-1212 $phone = new bbdbPhone("\"$bname\" 0 $1 $2"); } else { ## Anything else (International) $phone = new bbdbPhone("\"$bname\" \"$val\""); } $lrec->add_phone($phone); } elsif ($btype eq "note") { if ($bname ne 'notes') { $lrec->{'notes'}{$bname} = $val; } # $lrec->{'notes'}{'notes'} was already handled above. } elsif ($btype eq "net") { my @email = @{$lrec->{'email'}}; if (!scalar(@email)) { $lrec->{'email'} = [ $val ]; } elsif ($email[0] ne $val) { unshift(@{$lrec->{'email'}}, $val); } } elsif ($btype eq "aka") { my @aka = split(/, */,$val); $lrec->{'aka'} = \@aka; } else { print STDERR "Unknown bbdb-type: $btype\n"; } } } $lrec->{'notes'}{'timestamp'} = strftime("%Y-%m-%d", localtime); $lrec->{'notes'}{'creation-date'} = strftime("%Y-%m-%d", localtime) if ($is_new_rec); $lrec->{'notes'}{'pilot-id'} = $map->toString(); # push the changes back to the various pilot records. # this will also update the hash values. $pilotdb->syncRec($lrec); return $lrec; } sub toString { my $self = shift; my $rec = shift; my %hold; foreach my $f (@{$self->{'no-hash-fields'}}) { $hold{$f} = $rec->{'notes'}{$f} if (defined $rec->{'notes'}{$f}); delete $rec->{'notes'}{$f}; } my $str = $rec->ePStr(); foreach my $f (keys %hold) { $rec->{'notes'}{$f} = $hold{$f}; } return $str; } sub output { my $self = shift; my $rec = shift; my $fn = $rec->{'fname'}; my $ln = $rec->{'lname'}; my $org = $rec->{'org'}; my $str; $fn = "" if !defined($fn); $ln = "" if !defined($ln); $org = "" if !defined($org); $str = $fn; $str .= " " if ($str && $ln); $str .= $ln; $str .= ", " if ($str && $org); $str .= $org; if (!$str) { $str = ""; if (defined $rec->{'notes'}{'pilot-id'}) { my $map = new pilotBBDBMap($rec, $self->{'translate'}); my @ids; foreach my $mrec (@{$map->{'records'}}) { push(@ids, $mrec->{'id'}); } $str .= " - " . join(", ", @ids); } } $self->getHost()->output($str . "\n"); } sub update { my $self = shift; if (!defined $self->{'in bbdb read'}) { return $self->localHashDB::update(); } # we are in the bbdb read so call status instead of 'update'. $self->{'host'}->status("Reading BBDB Records", int(100*(($self->{'in bbdb count'}++)/ $self->{'in bbdb read'}))); } 1; SyncBBDB-2.6/SyncBBDB/pilotBBDB.pm0000644000076400007640000002342210425462100015637 0ustar kaplankaplan#------------------------------------------------------------------------------ # $Date: 2006/05/01 19:40:48 $ # RCS: $Id: pilotBBDB.pm,v 1.4 2006/05/01 19:40:48 aaronkaplan Exp $ #------------------------------------------------------------------------------ package pilotBBDB; use strict; require SyncUtil::pilotHashDB; @pilotBBDB::ISA = qw(pilotHashDB); sub new { my $type = shift; my $self = pilotHashDB->new(shift, shift, shift, shift); bless ($self, $type); $self->{'translate'} = shift; my $db = $self->{'dlp'}->open("AddressDB"); $self->{'db'} = $db; my $appInfo = $db->getAppBlock(); $self->{'appInfo'} = $appInfo; $self->{'CategoryNames'} = $appInfo->{'categoryName'}; my @CategoryNames = @{$self->{'CategoryNames'}}; my $i = 0; foreach my $cat (@CategoryNames) { $self->{'CategoryHash'}{$cat} = $i++; } $self->{'phoneLabels'} = $appInfo->{'phoneLabel'}; my @phoneLabels = @{$self->{'phoneLabels'}}; $i=0; foreach my $label (@phoneLabels) { $self->{'phoneLabelsHash'}{$label} = $i++; } $self->{'labels'} = $appInfo->{'label'}; my @labels = @{$self->{'labels'}}; $i=0; foreach my $label (@labels) { $self->{'labelsHash'}{$label} = $i++; } $self->{'Fields'} = [ 'Last', 'First', 'Company', 'Field1', 'Field2', 'Field3','Field4', 'Field5', 'Address', 'City', 'State', 'Zip', 'Country', 'Title', 'Custom1', 'Custom2', 'Custom3', 'Custom4', 'Note' ]; $i=0; foreach my $f (@{$self->{'Fields'}}) { ## map of fields to indexes in entry... $self->{'FieldMap'}{$f} = $i++; } $self; } sub name { my $self = shift; return "Pilot Address Book"; } sub syncRec { my $self = shift; my $lrec = shift; my $map = new pilotBBDBMap($lrec, $self->{'translate'}); my %origIDS; foreach my $rec (@{$map->{'records'}}) { # break any existing references... $self->{'hashObj'}->setLocalHash($rec->{'id'}, undef); delete $self->{'localDB'}->{'hash'}{$rec->{'id'}}; # remember we were associated with this id... $origIDS{$rec->{'id'}} = 1; } # Update the mapping.. $map->updateMappingFromBBDB($self); # make sure all the pilot records we think exist actually do exist. # Even if this means we need to create them... foreach my $rec (@{$map->{'records'}}) { my $prec = $self->getRec($rec->{'id'}); if (! defined $prec) { $prec = $self->newRec(); $rec->{'id'} = $prec->{'id'}; } # we continue to reference these ids.. delete $origIDS{$rec->{'id'}}; } # This is the list of ids we used to reference but don't anymore... foreach my $id (keys %origIDS) { $self->deleteRec($id); $self->{'localDB'}->deleteRec($id); } $lrec->{'notes'}{'pilot-id'} = $map->toString(); my $lhash = $self->{'localDB'}->hashRecord($lrec); foreach my $rec (@{$map->{'records'}}) { # Restore the new references $self->{'hashObj'}->setLocalHash($rec->{'id'}, $lhash); $self->{'localDB'}->{'hash'}{$rec->{'id'}} = $lrec; } foreach my $rec (@{$map->{'records'}}) { my $prec = $self->getRec($rec->{'id'}); ## Most categoryy setup code is in pilotBBDBMap.pm my $category = $rec->{'category'}; ## fallback case.. $category = "Unfiled" if (not $category); $prec->{'category'} = 0; # also 'Unfiled' if (defined $self->{'CategoryHash'}{$category}) { $prec->{'category'} = $self->{'CategoryHash'}{$category}; } # to set which phone is shown in the one line display... # Standard stuff $self->setField($prec, 'Last', $lrec->{'lname'}); $self->setField($prec, 'First', $lrec->{'fname'}); $self->setField($prec, 'Company', $lrec->{'org'}); $self->setField($prec, 'Title', $lrec->{'notes'}{'title'} || ""); $self->setField($prec, 'Note', $lrec->{'notes'}{'notes'} || ""); if (defined $rec->{'name'} && defined $lrec->{'addrH'}{$rec->{'name'}}) { my $addr = $lrec->{'addrH'}{$rec->{'name'}}; $self->setField($prec, 'Address', join("\xA", @{$addr->{'street'}})); $self->setField($prec, 'City', $addr->{'city'}); $self->setField($prec, 'State', $addr->{'state'}); if (defined $addr->{'zip'}) { $self->setField($prec, 'Zip', join(' ',@{$addr->{'zip'}})); } else { $self->setField($prec, 'Zip', ""); } $self->setField($prec, 'Country', $addr->{'country'}); } else { $self->setField($prec, 'Address', ""); $self->setField($prec, 'City', ""); $self->setField($prec, 'State', ""); $self->setField($prec, 'Zip', ""); $self->setField($prec, 'Country', ""); } # Empty everything out... $self->setField($prec, 'Field1', ""); $self->setField($prec, 'Field2', ""); $self->setField($prec, 'Field3', ""); $self->setField($prec, 'Field4', ""); $self->setField($prec, 'Field5', ""); $prec->{'phoneLabel'}[0] = 0; $prec->{'phoneLabel'}[1] = 1; $prec->{'phoneLabel'}[2] = 2; $prec->{'phoneLabel'}[3] = 3; $prec->{'phoneLabel'}[4] = 4; $self->setField($prec, 'Custom1', ""); $self->setField($prec, 'Custom2', ""); $self->setField($prec, 'Custom3', ""); $self->setField($prec, 'Custom4', ""); my $first=1; foreach my $ent (@{$rec->{'mappings'}}) { my $btype = $ent->{'bbdb-type'}; my $bname = $ent->{'bbdb-name'}; my $val = ""; if ($btype eq "phone") { $val = $lrec->{'phoneH'}{$bname}->phoneString(); } elsif ($btype eq "note") { if ($bname ne 'notes') { $val = $lrec->{'notes'}{$bname}; } # $lrec->{'notes'}{'notes'} was already handled above. } elsif ($btype eq "net") { my @email = @{$lrec->{'email'}}; #$val = join("\xA",@email); $val = $email[0]; } elsif ($btype eq "aka") { my @aka = @{$lrec->{'aka'}}; $val = join(", ",@aka); } else { print "Unknown bbdb-type\n"; } # Set the fields value... $self->setField($prec, $ent->{'pilot'}, $val); if (defined $ent->{'pilot-label'}) { if ($ent->{'pilot'} =~ m/Field(.)/) { my $idx = $1; $idx--; # Set the label for the field.... $prec->{'phoneLabel'}[$idx] = $ent->{'pilot-label'}; if ($first) { # Set the one-line phone to the first entry... # This really needs better support so I can # remember if it was changed on the pilot... $prec->{'showPhone'} = $idx; $first = 0; } } } } # Send the modified record back to the Pilot... $self->setRec($prec); # Also updates Hash } } sub printRec { my $self = shift; my $prec = shift; my @fields = ('Last', 'First', 'Title', 'Company', 'Address', 'City', 'State', 'Zip', 'Country', 'Field1', 'Field2', 'Field3', 'Field4', 'Field5', 'Custom1', 'Custom2', 'Custom3', 'Custom4', 'Note'); foreach my $f (@fields) { print $f. ": " . $self->getField($prec, $f) . "\n"; } print "Lables: " . join(", ", @{$prec->{'phoneLabel'}}) . "\n"; } sub newRec { my $self = shift; my $ret = PDA::Pilot::AddressDatabase->record; $ret->{'id'} = 0; $ret->{'category'} = 0; $ret->{'entry'} = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""]; $ret->{'phoneLabel'} = [0 .. 4]; $ret->{'secret'} = 0; $self->setRec($ret); return $ret; } sub dupRec { my $self = shift; my $prec = shift; my $lrec = shift; my $lcpy = shift; my $ret = PDA::Pilot::AddressDatabase->record; $ret->{'id'} = 0; ## disassociate from original record... $ret->{'category'} = $prec->{'category'}; my @dup = (); push(@dup, @{$prec->{'entry'}}); $ret->{'entry'} = [@dup]; @dup = (); push(@dup, @{$prec->{'phoneLabel'}}); $ret->{'phoneLabel'} = [@dup]; $ret->{'secret'} = $prec->{'secret'}; $self->setRec($ret); # Get a record ID... my $map = new pilotBBDBMap($lrec, $self->{'translate'}); my $mapRec = $map->getMapForID($prec->{'id'}); # setup new mapping for local rec. $map = new pilotBBDBMap($lcpy, $self->{'translate'}); push(@{$map->{'records'}}, $mapRec->copy($ret->{'id'})); $lcpy->{'notes'}{'pilot-id'} = $map->toString(); return $ret; } sub categoryStr { my $self = shift; my $rec = shift; return ${$self->{'CategoryNames'}}[$rec->{'category'}]; } sub getPhoneLabel { my $self = shift; my $indx = shift; return $self->{'phoneLabels'}[$indx]; } sub getCustLabel { my $self = shift; my $indx = shift; return $self->{'labels'}[$indx+$self->getFieldIndex('Custom1')]; } sub getFieldIndex { my $self = shift; my $field = shift; return $self->{'FieldMap'}{$field}; } sub getField { my $self = shift; my $rec = shift; my $field = shift; my $i = $self->getFieldIndex($field); if (!defined $i) { print "Unkown Pilot field: $field\n" ; my $x=0; my ($pack, $file, $line, $subname); while (($pack, $file, $line, $subname) = caller($x++)) { print "\tfile:$file line:$line\n"; } } return $rec->{'entry'}[$i]; } sub setField { my $self = shift; my $rec = shift; my $field = shift; my $val = shift; my $indx = $self->getFieldIndex($field); print "Field: $field\n" if (not defined $indx) ; $rec->{'entry'}[$indx] = $val; } sub output { my $self = shift; my $rec = shift; my $fn = $self->getField($rec, 'First'); my $ln = $self->getField($rec, 'Last'); my $org = $self->getField($rec, 'Company'); my $str = ""; $fn = "" if !defined($fn); $ln = "" if !defined($ln); $org = "" if !defined($org); $str = $fn; $str .= " " if ($str && $ln); $str .= $ln; $str .= ", " if ($str && $org); $str .= $org; $str ||= " - " . $rec->{'id'}; $self->{'host'}->output($str . "\n"); } 1; SyncBBDB-2.6/SyncBBDB/pilotBBDBFieldMap.pm0000664000076400007640000000302307371240004017241 0ustar kaplankaplan#------------------------------------------------------------------------------ # $Date: 2001/11/04 13:17:56 $ # RCS: $Id: pilotBBDBFieldMap.pm,v 1.1.1.1 2001/11/04 13:17:56 tdeweese Exp $ #------------------------------------------------------------------------------ package pilotBBDBFieldMap; use strict; sub new { my $type = shift; my $self = {}; bless($self, $type); $self->{'bbdb-type'} = shift; # one of addr, phone, note $self->{'bbdb-name'} = shift; # anything $self->{'pilot'} = shift; # Field1-5, Custom1-4, Title, Note $self->{'pilot-label'} = shift if (scalar @_); # for Fields 0-7 'label' return $self; } sub copy { my $self = shift; my $ret; if (defined $self->{'pilot-label'}) { return new pilotBBDBFieldMap($self->{'bbdb-type'}, $self->{'bbdb-name'}, $self->{'pilot'}, $self->{'pilot-label'}); } return new pilotBBDBFieldMap($self->{'bbdb-type'}, $self->{'bbdb-name'}, $self->{'pilot'}); } sub getBBDB { my $self = shift; return $self->{'bbdb-type'} . ":" . $self->{'bbdb-name'}; } sub getPilot { my $self = shift; if (defined $self->{'pilot-label'}) { return $self->{'pilot'} . ":" . $self->{'pilot-label'}; } else { return $self->{'pilot'}; } } sub getPilotType { my $self = shift; my $str = $self->{'pilot'}; return "Title" if ($str eq "Title"); return "Note" if ($str eq "Note"); return "Field" if ($str =~ m/Field/); return "Custom" if ($str =~ m/Custom/); } 1; SyncBBDB-2.6/SyncBBDB/pilotBBDBMap.pm0000644000076400007640000003646110425462100016304 0ustar kaplankaplan#------------------------------------------------------------------------------ # $Date: 2006/05/01 19:40:48 $ # RCS: $Id: pilotBBDBMap.pm,v 1.3 2006/05/01 19:40:48 aaronkaplan Exp $ #------------------------------------------------------------------------------ package pilotBBDBMap; use strict; use bbdb::bbdb; use bbdb::bbdbRecord; use SyncBBDB::pilotBBDBRecMap; use SyncBBDB::pilotBBDBFieldMap; my @pilot_fields = ("Field1", "Field2", "Field3", "Field4", "Field5", "Custom1", "Custom2", "Custom3", "Custom4", "Title", "Note"); # Regex for a quoted string... my $qstrRE = "\"((?:[^\\\\\"]+|\\\\.)*)\""; ## Phone number stuff... my $parea = '1?\s*[-.\(]?\s*([2-9][0-9][0-9])[-.\)\s]+'; my $pmain = '([1-9][0-9][0-9])[-.\s]*([0-9][0-9][0-9][0-9])'; my $pext = '[-.x\s]+(\d+)'; my $phoneRE1 = "^\\s*" . $parea . $pmain . $pext . "\\s*\$"; my $phoneRE2 = "^\\s*" . $parea . $pmain . "\\s*\$"; my $phoneRE3 = "^\\s*" . $pmain . $pext . "\\s*\$"; my $phoneRE4 = "^\\s*" . $pmain . "\\s*\$"; my $phoneRE5 = "^[-0-9 EeXxtT.()+]*\$"; sub new { my $type = shift; my $self = {}; bless($self, $type); $self->{'bbdb'} = shift; $self->{'translate'} = shift; my $val = $self->{'bbdb'}{'notes'}{'pilot-id'} || ""; $self->parse($val); return $self; } sub toString { my $self = shift; my $ret; foreach my $rec (@{$self->{'records'}}) { my $id = $rec->{'id'}; my $cat = $rec->{'category'}; my $name = "-NO-ADDRESS-"; if (defined $rec->{'name'}) { $name = $rec->{'name'}; } $ret .= $id." \"".$cat."\" \"".$name."\"\n"; my @ents; foreach my $ent (@{$rec->{'mappings'}}) { push(@ents, " \"".$ent->getPilot()."\" \"".$ent->getBBDB()."\""); } $ret .= join("\n", sort(@ents)) . "\n"; } $self->updateSkipped(); if (scalar(@{$self->{'skipped'}})) { $ret .= " BBDB-Unused: \""; $ret .= join("\",\"",@{$self->{'skipped'}}) . "\""; } else { # Kill the 'extra' newline... chomp($ret); } return $ret; } sub parse { my $self = shift; my $str = shift; $self->{'records'} = []; $self->{'skipped'} = []; my $recMap; my @lines = split(/\n/, $str); # print "\n\nStarting Parse\n"; # This supports the new Quoted form as well as the unquoted form. # Eventually I will remove support for the unquoted form (it's dangerous) my $identRE = "(?:$qstrRE|([^\"\\s]+))"; foreach my $l (@lines) { # print "Line: '$l'\n"; if ($l =~ /^\s*([0-9]+)\s*$/) { # Old style pilot-id... my $id = $1; my $name = undef; foreach my $l (@{$self->{'bbdb'}{'addr'}}) { if ($l->{'name'} =~ m/^\*/) { $name = $l->{'name'}; last; } } if ((!defined $name) && (scalar (@{$self->{'bbdb'}{'addr'}}))) { $name = $self->{'bbdb'}{'addr'}[0]->{'name'}; } my $cat = undef; # Get the category mapping, and delete it when done. if (defined $self->{'bbdb'}{'notes'}{'category'}) { my @cats = split(/\s+/,$self->{'bbdb'}{'notes'}{'category'}); $cat = $cats[0]; } elsif (defined $self->{'bbdb'}{'org'}) { ## Special cases my $org = $self->{'bbdb'}{'org'}; $cat = "Kodak" if ($org eq "Eastman Kodak"); $cat = "Business" if ($org eq "Sun Microsystems"); } if ((!defined $cat) && defined $name) { # See if our address name is a category name... if (defined $self->{'CategoryHash'}{$name}) { $cat = $self->{'CategoryHash'}{$name}; } } $cat = "Unfiled" if (!defined $cat); if (defined $name) { $recMap = new pilotBBDBRecMap($id, $cat, $name); } else { $recMap = new pilotBBDBRecMap($id, $cat); } push(@{$self->{'records'}}, $recMap); last; } elsif ($l=~/^\s*([0-9]+)\s+$identRE\s+$identRE\s*$/) { # start of new my $id = $1; my $cat = $2 || $3; my $name = $4 || $5; if ($name ne "-NO-ADDRESS-") { $recMap = new pilotBBDBRecMap($id, $cat, $name); } else { $recMap = new pilotBBDBRecMap($id, $cat); } push(@{$self->{'records'}}, $recMap); } elsif ($l =~ m/^\s*BBDB-Unused:\s*"(.*)"/) { # Quoted strings... my $fields = "\"".$1."\""; while ($fields =~ m/$qstrRE/) { $fields = $'; my $field = $1; push(@{$self->{'skipped'}}, $field); } } elsif ($l =~ m/^\s*BBDB-Unused:\s*/) { # Unquoted strings... my $fields = $'; push(@{$self->{'skipped'}}, split(/\s*,\s*/,$fields)); } elsif ($l=~m/^\s*$identRE\s+$identRE\s*$/) { my $pinfo = $1 || $2; my $binfo = $3 || $4; my $ent = undef; if ($binfo =~ m/([^:]+):(.+)/) { my $btype = $1; my $bname = $2; if ($pinfo =~ m/([^:]+):(.+)/) { my $pname = $1; my $pval = $2; $ent = new pilotBBDBFieldMap($btype, $bname, $pname, $pval); } else { $ent = new pilotBBDBFieldMap($btype, $bname, $pinfo); } if (!defined $recMap) { print STDERR "Name: " . $self->{'bbdb'}{'fname'} . " " . $self->{'bbdb'}{'lname'} . "\n"; } else { $recMap->addMapping($ent); } } else { # Bad line.... What to do... } } } } sub updateSkipped { my $self = shift; my %used = %{$self->getUsedBBDBFields(undef)}; my %all = %{$self->getAllBBDBFields()}; foreach my $f (keys %used) { delete $all{$f}; } my @skip = keys %all; $self->{'skipped'} = \@skip; } sub dup { my $self = shift; } sub getAllBBDBFields { my $self = shift; my $rec = $self->{'bbdb'}; my %fields; if (defined $rec->{'email'} && scalar(@{$rec->{'email'}})) { $fields{"net:0"} = 1; } if (defined $rec->{'aka'} && scalar(@{$rec->{'aka'}})) { $fields{"aka:0"} = 1; } foreach my $addr (@{$rec->{'addr'}}) { $fields{"addr:" . $addr->{'name'}} = 1; } foreach my $phone (@{$rec->{'phone'}}) { $fields{"phone:" . $phone->{'title'}} = 1; } foreach my $note (keys %{$rec->{'notes'}}) { next if $note eq 'notes'; $fields{"note:$note"} = 1; } return \%fields; } sub getUsedBBDBFields { my $self = shift; my $incSkip = shift; my %ret; $ret{'note:pilot-id'} = 1; foreach my $rec (@{$self->{'records'}}) { if (defined $rec->{'name'}) { $ret{"addr:" . $rec->{'name'}} = 1; } my %rec = %{$rec->getUsedBBDBFields()}; foreach my $f (keys %rec) { $ret{$f} = 1; } } if ($incSkip) { foreach my $f (@{$self->{'skipped'}}) { $ret{$f} = 1; } } return \%ret; } sub getNewBBDBFields { my $self = shift; my %ret = %{$self->getAllBBDBFields()}; my %used = %{$self->getUsedBBDBFields(1)}; foreach my $f (keys %used) { delete $ret{$f}; } return \%ret; } sub getDeletedBBDBFields { my $self = shift; my %all = %{$self->getAllBBDBFields()}; my %used = %{$self->getUsedBBDBFields(1)}; my %ret; foreach my $f (keys %used) { $ret{$f} = 1 if (!defined $all{$f}); } return \%ret; } sub removeBBDBMapping { my $self = shift; my $field = shift; my @recs = @{$self->{'records'}}; my @result = (); foreach my $rec (@recs) { if ((defined $rec->{'name'}) && ($field eq "addr:" . $rec->{'name'})) { # other addresses so non-address info will still # exist in palm. next if (scalar(@recs) > 1); # No other addresses so we want to 'metamorphise' # into a non-address record: delete $rec->{'name'}; } else { $rec->removeBBDBMapping($field); } push(@result, $rec); } $self->{'records'} = \@result; } sub removePilotMapping { my $self = shift; my $field = shift; my @recs = @{$self->{'records'}}; foreach my $rec (@recs) { $rec->removePilotMapping($field); } } sub getAllPilotFields { my $pbbdb = shift; my $prec = shift; my %ret; foreach my $f (@pilot_fields) { my $val = $pbbdb->getField($prec, $f); next if (!defined $val || ! $val); $ret{$f} = 1; } return \%ret; } # Similar to getUsedPilotFields except it annontates Fields[1-5] with # the label that is currently choosen. sub getAllPilotFieldsAnnotated { my $pbbdb = shift; my $prec = shift; my %ret; foreach my $f (@pilot_fields) { my $val = $pbbdb->getField($prec, $f); next if (!defined $val || ! $val); if ($f !~ m/Field/) { $ret{$f} = 1; } else { my $idx = $'; $idx--; my $lbl = $prec->{'phoneLabel'}[$idx]; $ret{$f . ":" . $lbl} = 1; } } return \%ret; } sub getUsedPilotFields { my $self = shift; my $pbbdb = shift; my $id = shift; my %ret; foreach my $rec (@{$self->{'records'}}) { next if ($rec->{'id'} ne $id); foreach my $ent (@{$rec->{'mappings'}}) { if (defined $ent->{'pilot-label'}) { $ret{$ent->{'pilot'} . ":" . $ent->{'pilot-label'}} = 1; } else { $ret{$ent->{'pilot'}} = 1; } } } return \%ret; } sub getNewPilotFields { my $self = shift; my $pbbdb = shift; my $prec = shift; my %ret = %{getAllPilotFieldsAnnotated($pbbdb, $prec)}; my %used = %{$self->getUsedPilotFields($pbbdb, $prec->{'id'})}; foreach my $f (keys %used) { delete $ret{$f}; } return \%ret; } sub getDeletedPilotFields { my $self = shift; my $pbbdb = shift; my $prec = shift; my %all = %{getAllPilotFieldsAnnotated($pbbdb, $prec)}; my %used = %{$self->getUsedPilotFields($pbbdb, $prec->{'id'})}; my %ret; foreach my $f (keys %used) { $ret{$f} = 1 if (!defined $all{$f}); } return \%ret; } sub getMapForID { my $self = shift; my $id = shift; foreach my $rec (@{$self->{'records'}}) { return $rec if ($id eq $rec->{'id'}); } return undef; } sub delMapForID { my $self = shift; my $id = shift; my $ret = undef; my @recs; foreach my $rec (@{$self->{'records'}}) { if ($id eq $rec->{'id'}) { $ret = $rec; } else { push(@recs, $rec); } } $self->{'records'} = \@recs; return $ret; } sub getMapForName { my $self = shift; my $name = shift; foreach my $rec (@{$self->{'records'}}) { return $rec if ($name eq $rec->{'name'}); } return undef; } sub updateMappingFromBBDB { # BBDB record has changed update the mapping to the Pilot Record # to include any new fields in the BBDB record and to remove any # deleted fields from the BBDB record. my $self = shift; my $pbbdb = shift; my %deleted = %{$self->getDeletedBBDBFields()}; my %new = %{$self->getNewBBDBFields()}; foreach my $f (keys %deleted) { $self->removeBBDBMapping($f); } # print STDERR "New: " . join(", ", keys %new) . "\n"; my @newRecs = (); foreach my $f (keys %new) { $f =~ m/([^:]+):(.*)/; my $type = $1; my $name = $2; if ($type eq "addr") { my $mapRec = $self->newAddressFromBBDB($pbbdb, $name); push(@newRecs, $mapRec) if (defined $mapRec); } } if ((scalar(@{$self->{'records'}}) == 0) && (scalar(@newRecs) == 0)) { # create a new pilot record with no associated address if we don't # have one by now. my $mapRec = $self->newAddressFromBBDB($pbbdb, undef); push(@newRecs, $mapRec) if (defined $mapRec); } foreach my $rec (@{$self->{'records'}}) { $self->addFieldsToRecord($pbbdb, $rec, \%new); } push(@{$self->{'records'}},@newRecs); } # Add a new address from BBDB which means creating a new Pilot record # and adding field mappings to it... sub newAddressFromBBDB { my $self = shift; my $pbbdb = shift; my $name = shift; # One pilot record with no associated addr. # So associate the new addr with it... # I don't do this if there is more than one pilot rec since # if one (or more) of them aren't assoicated with BBDB addrs # it is probably intentional. if (scalar(@{$self->{'records'}}) == 1) { my $rec = $self->{'records'}[0]; if (!defined $rec->{'name'}) { $rec->{'name'} = $name; return undef; # no new record. } } # create a new pilot record to sync this address with... my $prec = $pbbdb->newRec(); my $rec = new pilotBBDBRecMap($prec->{'id'}, "Unfiled", $name); $self->addFieldsToRecord($pbbdb, $rec, $self->getAllBBDBFields()); return $rec; } sub addFieldsToRecord { my $self = shift; my $pbbdb = shift; my $rec = shift; my $tmp = shift; my %new = %{$tmp}; if (defined $rec->{'name'}) { my $name = $rec->{'name'}; my $pname = $self->{'translate'}->getForPilot($name); if (defined $new{"phone:" . $name}) { # Should become primary phone for this record... $rec->addBBDB($pbbdb, "phone", $name, $pname, 1); } if (defined $new{"note:" . $name}) { # Should become primary custom for this record... $rec->addBBDB($pbbdb, "note", $name, $pname, 1); } } my $hasNet = (defined $new{"net:0"}); foreach my $n (keys %new) { # Only One field left and we have a net, make sure we include it. last if (($hasNet) && $rec->leftFree($pbbdb) == 1); # Get the BBDB record type and the name.. $n =~ m/^([^:]+):(.*)$/; my $type = $1; my $name = $2; # Don't handle net's right now... next if ($type eq "net"); # Don't handle address here... next if ($type eq "addr"); # check if we already handled this in the first pass... next if ((defined $rec->{'name'}) && ($rec->{'name'} eq $name)); if ($type eq "aka") { my $pname = $self->{'translate'}->getForPilot("aka"); $rec->addBBDB($pbbdb, "aka", "0", $pname); next; } # Try adding the field to the record. my $pname = $self->{'translate'}->getForPilot($name); $rec->addBBDB($pbbdb, $type, $name, $pname); } if ($hasNet) { my $pname = $self->{'translate'}->getForPilot("net"); $rec->addBBDB($pbbdb, "net", "0", $pname, 1); } } sub updateMappingFromPilot { my $self = shift; my $pbbdb = shift; my $lrec = $self->{'bbdb'}; my @recs; foreach my $rec (@{$self->{'records'}}) { my $prec = $pbbdb->getRec($rec->{'id'}); # Record was deleted, so this mapping goes away... next if (!defined $prec); my %deleted = %{$self->getDeletedPilotFields($pbbdb, $prec)}; my %new = %{$self->getNewPilotFields($pbbdb, $prec)}; # print STDERR "Pilot New: " . join(", ", keys %new) . # "\n Del:: " . join(", ", keys %deleted) . "\n"; foreach my $f (keys %deleted) { $self->removePilotMapping($f); } my @newEnt; foreach my $f (keys %new) { my $pname = $f; my $pval = undef; if ($pname =~ m/(.*):(.*)/) { $pname = $1; $pval = $2; } my $bname = $self->{'translate'}->getBBDBName($pbbdb, $f); # print "Info: $pname->$bname\n"; if (($bname eq "title") || ($bname eq "note")){ push(@newEnt, new pilotBBDBFieldMap("note", $bname, $pname)); next; } if ($bname eq "net") { push(@newEnt, new pilotBBDBFieldMap("net", "0", $pname, $pval)); next; } if ($bname eq "aka") { push(@newEnt, new pilotBBDBFieldMap("aka", "0", $pname, $pval)); next; } my $field = $pbbdb->getField($prec, $pname); if ($field =~ m/$phoneRE5/) { push(@newEnt, new pilotBBDBFieldMap("phone", $bname, $pname, $pval)); next; } else { # Make sure that bname is a valid lisp 'identifier' otherwise # BBDB won't be able to read it... $bname =~ s/[ ]/-/g; $bname =~ s/[\'\"]//g; $bname =~ s/[\?\[\]\\\(\)\;\#]/_/g; push(@newEnt, new pilotBBDBFieldMap("note", $bname, $pname, $pval)); next; } } push(@{$rec->{'mappings'}}, @newEnt); push(@recs, $rec); } $self->{'records'} = \@recs; } 1; SyncBBDB-2.6/SyncBBDB/pilotBBDBRecMap.pm0000664000076400007640000000776307371240005016747 0ustar kaplankaplan#------------------------------------------------------------------------------ # $Date: 2001/11/04 13:17:57 $ # RCS: $Id: pilotBBDBRecMap.pm,v 1.1.1.1 2001/11/04 13:17:57 tdeweese Exp $ #------------------------------------------------------------------------------ package pilotBBDBRecMap; use strict; use SyncBBDB::pilotBBDBFieldMap; sub new { my $type = shift; my $self = {}; $self->{'id'} = shift; $self->{'category'} = shift; $self->{'name'} = shift if (scalar @_); $self->{'mappings'} = []; bless($self, $type); return $self; } sub copy { my $self = shift; my $nid = shift; my $ret = new pilotBBDBRecMap($nid || 0, $self->{'category'}); $ret->{'name'} = $self->{'name'} if (defined $self->{'name'}); foreach my $ent (@{$self->{'mappings'}}) { push(@{$ret->{'mappings'}}, $ent->copy()); } return $ret; } sub isFieldFree { my $self = shift; my $field = shift; foreach my $ent (@{$self->{'mappings'}}) { return "" if ($ent->{'pilot'} eq $field); } return 1; } sub firstFree { my $self = shift; my $pbbdb = shift; my $type = shift; my $pname = shift; my $ret = undef; if (($type eq "Title") || ($type eq "Note")) { return $type if ($self->isFieldFree($type)); } elsif ($type eq "Field") { foreach my $i (1..5) { if ($self->isFieldFree("Field".$i)) { if (defined $pbbdb->{'phoneLabelsHash'}{$pname}) { return ($type.$i, $pbbdb->{'phoneLabelsHash'}{$pname}); } else { return ($type.$i, undef); } } } } elsif (($type eq "Custom") && (defined $pname)) { # print ("Lables: '" . # join("', '", keys %{$pbbdb->{'labelsHash'}}). # "'\n"); if (defined $pbbdb->{'labelsHash'}{$pname}) { my $num = $pbbdb->{'labelsHash'}{$pname}; my $field = $pbbdb->{'Fields'}[$num]; return $field if ($self->isFieldFree($field)); } } elsif ($type eq "Custom") { # Not given a name so we are just looking for an open custom field.. # regardless of what it's label is... foreach my $i (1..4) { return "Custom".$i if ($self->isFieldFree("Custom".$i)); } } return undef; } sub leftFree { my $self = shift; my $pbbdb = shift; my $num=0; foreach my $i (1..5) { $num++ if ($self->isFieldFree("Field".$i)); } return $num; } sub addBBDB { my $self = shift; my $pbbdb = shift; my $btype = shift; my $bname = shift; my $pname = shift; my $force = shift; my $ptype; if (defined $pbbdb->{'phoneLabelsHash'}{$pname}) { $ptype = "Field"; } else { $ptype = "Custom"; } if (($ptype eq "Field") || ($force && ($btype eq "phone"))) { my ($p,$pl) = $self->firstFree($pbbdb, "Field", $pname); if (defined $p) { if (defined $pl) { push(@{$self->{'mappings'}}, new pilotBBDBFieldMap($btype, $bname, $p, $pl)); } elsif ($force) { push(@{$self->{'mappings'}}, new pilotBBDBFieldMap($btype, $bname, $p, -1)); } } } else { my $free = $self->firstFree($pbbdb, $ptype, $pname); if (defined $free) { push(@{$self->{'mappings'}}, new pilotBBDBFieldMap($btype, $bname, $free)); } elsif ($force) { $free = $self->firstFree($pbbdb, $ptype); push(@{$self->{'mappings'}}, new pilotBBDBFieldMap($btype, $bname, $free)); } } } sub addMapping { my $self = shift; push(@{$self->{'mappings'}}, shift); } sub getUsedBBDBFields { my $self = shift; my %ret; foreach my $ent (@{$self->{'mappings'}}) { $ret{$ent->getBBDB()} = 1; } return \%ret; } sub removeBBDBMapping { my $self = shift; my $field = shift; my @result; foreach my $ent (@{$self->{'mappings'}}) { next if ($ent->getBBDB() eq $field); push(@result, $ent); } $self->{'mappings'} = \@result; } sub removePilotMapping { my $self = shift; my $field = shift; my @result; foreach my $ent (@{$self->{'mappings'}}) { next if ($ent->getPilot() eq $field); push(@result, $ent); } $self->{'mappings'} = \@result; } 1; SyncBBDB-2.6/SyncBBDB/pilotBBDBTranslate.pm0000664000076400007640000000564307371240005017530 0ustar kaplankaplan#------------------------------------------------------------------------------ # $Date: 2001/11/04 13:17:57 $ # RCS: $Id: pilotBBDBTranslate.pm,v 1.1.1.1 2001/11/04 13:17:57 tdeweese Exp $ #------------------------------------------------------------------------------ package pilotBBDBTranslate; use FileHandle; use strict; sub new { my $type = shift; my $self = {}; bless($self, $type); $self->{'host'} = shift; $self->{'file'} = shift; $self->{'p2b'} = {}; $self->{'b2p'} = {}; $self->readFile(); return $self; } sub readFile { my $self = shift; if ((! defined $self->{'file'}) || (! -f $self->{'file'})) { # Give them the most important stuff. $self->addMapping("E-mail", "net"); $self->addMapping("Title", "title"); $self->addMapping("Note", "notes"); $self->addMapping("AKA", "aka"); $self->addMapping("WWW", "www"); return; } my $FILE = new FileHandle($self->{'file'}); while (<$FILE>) { next if (/^\s*\#/); next if (/^\s*\$/); chop; if (/^\s*([^\s]*)\s+(.*[^\s])/) { $self->addMapping($1, $2); } } } sub writeFile { my $self = shift; my $FILE =new FileHandle(">" .$self->{'file'}); if (!defined $FILE) { $self->{'host'}->output("Unable to rewrite translations file: " . $self->{'file'} . "\n"); return; } print $FILE "# This file describes the mapping between BBDB fields\n"; print $FILE "# and pilot fields.\n\n"; print $FILE "# The format is: \n"; print $FILE "# The pilot field may not contain a space,\n"; my %used; foreach my $p (keys %{$self->{'p2b'}}) { print $FILE $p . " " . $self->{'p2b'}{$p} . "\n"; $used{$self->{'p2b'}{$p}} = 1; } foreach my $b (keys %{$self->{'b2p'}}) { next if (defined $used{$b}); print $FILE $self->{'b2p'}{$b} . " " . $b . "\n"; } } sub addMapping { my $self = shift; my $pilot = shift; my $bbdb = shift; if (! defined $self->{'p2b'}{$pilot}) { $self->{'p2b'}{$pilot} = $bbdb; } if (! defined $self->{'b2p'}{$bbdb}) { $self->{'b2p'}{$bbdb} = $pilot; } } sub finish { my $self = shift; } sub getForBBDB { my $self = shift; my $pilot = shift; return $self->{'p2b'}{$pilot} || $pilot; } sub getForPilot { my $self = shift; my $bbdb = shift; # hack for old mapping scheme... return $' if ($bbdb =~ m/pilot-field-/); return $' if ($bbdb =~ m/pilot-phone-/); return $self->{'b2p'}{$bbdb} || $bbdb; } sub getBBDBName { my $self = shift; my $pbbdb = shift; my $field = shift; my $name; if ($field =~ m/^Field.:(.)/) { my $idx = $1; $name = $pbbdb->{'phoneLabels'}[$idx]; } elsif ($field =~ m/^Custom(.)/) { my $idx = $1; $idx--; $name = $pbbdb->getCustLabel($idx); } else { $name = $field; } if (defined $self->{'p2b'}{$name}) { return $self->{'p2b'}{$name} } else { return $name; } } 1; SyncBBDB-2.6/ChangeLog0000664000076400007640000001347410561723706014011 0ustar kaplankaplan2007-02-05 Aaron Kaplan * Release version 2.6 2006-01-22 Aaron Kaplan * Fix bug: when a bbdb record was split into multiple pilot records, changes to the notes field on the pilot weren't being propagated correctly. Bug reported by Alex Lancaster. 2006-01-22 Aaron Kaplan * Update installation instructions and contact info in README. 2005-09-11 Aaron Kaplan * Change the way we reference constants exported from libpisock, in preparation for pilot-link 0.12.0-pre5. Backwards compatibility is maintained. 2005-05-21 Aaron Kaplan * In log, display company name if first and last names are empty. 2005-05-20 Aaron Kaplan * Patch from David Goldberg: for empty name or company, use nil instead of "". With "", it becomes an "unfield" which bbdb won't let you edit. 2005-04-10 Aaron Kaplan * Release version 2.5. 2004-12-14 Aaron Kaplan * Fix major bug: if connection is dropped during sync, entries can be erroneously deleted. You could lose your whole address book this way! * Bring "bootstrap" up to date so that it works again. * Fix a couple of bugs that had been introduced post-2.3. The buggy code was never officialy released, but some people might have downloaded it via CVS. * correct call to $dlp->getUserInfo: it doesn't die on error, it just returns undef. * More fixes for non-ascii ISO-8859-1 characters. This is just a band-aid, we really need a more thorough solution that can handle Palms with different encodings, and different codings (utf-8 for example) on the unix side as well. 2001-10-10 Thomas DeWeese * Now only sets bbdb first name, last name and company if the pilot had values for them. * Ensures phone labels are set to valid values * Ensures overview field is set to the first field set by the sync (really needs more work). 2001-9-14 Thomas DeWeese * Fixed a bug so SyncBBDB will delete records from BBDB when deleted on the palm (introduced with 'pilot-ignore' support). 2000-12-03 Thomas DeWeese * Incorporated Non-ASCII fix from Aaron Kaplan (Thanks!) * Fixed a warning pertaining to the phone Regexps. * Added support for bbdb file format version 6 * Cleaned up bootstrap a little. * Now updates timestamp, and creates creation-date as appropriate. 2000-10-17 Thomas DeWeese * Now ignores bbdb records for purposed of syncing that have a note called 'pilot-ignore'. * Now works with an empty or non-existant BBDB file (nice for first time users who are building the bbdb file from the palm). * Now properly parses the SyncBBDB 'control' file when the pre/post sync scripts are empty (if you experienced this problem then you will likely need to fix the information in the GUI for it to 'take hold'). * Will now properly create a 'net' field in bbdb when the first e-mail is entered on the palm. * Cleaned up Category handling so it actually works properly when records are modified on the Palm. * Fixed a bug in the code that tries to pick palm fields to put bbdb notes into 2000-08-27 Thomas DeWeese * Switched the method of determining if a fast/slow sync is approprate. Now stores the 'thisSyncDate' in the ids file as the first line. 2000-08-26 Thomas DeWeese * Fixed a cosmetic bug in the pilot-id field when there aren't any unused bbdb fields. * Now gives status while reading the local BBDB file. This is generally pretty quick, 2-3 sec. for my ~700 entry bbdb file, but for very large bbdb files this should be nice. * Switched from 'tickle' to 'watchdog' around potentially time consuming steps. To be quite honest I don't know what watchdog is but since SyncMemo uses it for a similar purpose I assume it's the correct thing to do. * Cleaned up the startup code for SyncBBDB so the user doesn't need to create the SyncBBDB directory. 2000-08-06 Thomas E Deweese * Improved the way the Standalone host does status. * Changed the format of the pilot-id map. So that all field names are quoted this ensures that locations with spaces don't screw things up. * Fixed a bug dealing with new records from the palm that didn't have addresses (version 1.4) 2000-08-02 Thomas E Deweese * pilotHashDB no longer puts records with the 'delete' flag on into the 'modify' list for slow syncs. They will now only appear in the delete list. * Fixed a bug in the handling of deleted addresses from BBDB. It would remove the reference to the pilot record from the BBDB pilot-id field but it wouldn't actually delete the pilot record (now it does). * Did a fair amount of work on the Standalone Host which should help people using SyncBBDB outside of PilotManager (now does a fairly nice job of displaying status on a tty). * Added 'status' support to the SyncUtils so it now updates The status bar as things progress. Could still use some work (in particular when loading the BBDB file). 2000-07-31 Thomas DeWeese * Lots of changes to support complex mapping between BBDB and Pilot records. Most notably it supports associating multiple pilot records with each bbdb record. The majority of changes for the mapping are in the new files in SyncBBDB (pilotBBDB.*Map.pm, pilotBBDBTranslation.pm). 2000-07-18 Thomas DeWeese * Updated bbdb code for format 5 of bbdb. bbdbAddr 'street' is now a list. 2000-07-15 Thomas E Deweese * Zips are now always treated as strings. * Now splits zips on spaces (instead of non-numbers). Expanded PhoneRE5, and no matter what if it matches phoneRE5 it will bestored as a phone (possibly as an opaque string). Will no-longer create empty time-stamps if they didn't already exist. SyncBBDB-2.6/README0000664000076400007640000002641310436321621013103 0ustar kaplankaplanHow To Install: --------------- To use SyncBBDB as a stand-alone program, copy the bbdb, SyncBBDB and SyncUtil directories to someplace on the Perl include path, and copy the script "bootstrap" to someplace on your PATH. Run bootstrap -help for configuration options. To use SyncBBDB as a PilotManager conduit, either - If you're using PilotManager version 1.108 or higher, and you gave a -conduitdir argument to PilotManager's Setup.pl script, then copy the directories bbdb, SyncBBDB, and SyncUtil and the file SyncBBDB.pm to the conduitdir directory. - Otherwise, copy the bbdb, SyncBBDB, and SyncUtil directories to lib/perl5 inside the PilotManager directory, and copy the file SyncBBDB.pm to the top level of the PilotManager directory. Then run PilotManager, go to File->Properties, move SyncBBDB from the inactive list to the active list, and click Configure to configure it. Other than that things should take care of themselves. The first time you sync all your bbdb records will go to the pilot and vice versa. This may result in many duplicate records in which case you might want to use the 'bbdb-show-duplicate-records' function in bbdb to help merge things. Credits and Contact Information -------------------- SyncBBDB was written by Thomas DeWeese, and is now maintained by Aaron Kaplan. Up-to-date contact information can currently be found at http://syncbbdb2.sourceforge.net/ . Directions for users upgrading from 1.x --------------------------------------- The easiest way to fully 2.0ify your bbdb is: 1) Sync your palm one last time with the old version of the software. 2) Shut down PilotManager. 3) Install the 2.x version of SyncBBDB 4) Delete or move your ids file (the pilot mgr default: ~/.pilotmgr/SyncBBDB/ids._). 5) Start PilotManager (make sure the version for SyncBBDB is 2.x). 6) Sync your palm again. This will make the software think all your records in bbdb are modified. It will then proceed to merge all the records with the pilot records, in the process it will update all the 'pilot-id' fields in the bbdb records. If you don't do this up front, over time, as records are synched, the 'pilot-id' field will be updated 'as needed'. Personally I prefer/recommend the all at once especially since this will force the creation of the currently lacking multiple pilot records for multiple addresses (See below). Version history (see ChangeLog for details): Version 2.5 ----------- Aaron Kaplan takes over maintenance of SyncBBDB. Fixed major bug: if connection is dropped during sync, entries can be erroneously deleted. You could lose your whole address book this way! Bring "bootstrap" up to date so that it works again. More fixes for non-ASCII characters, although you may still have problems depending on the combination of encodings used on your Palm and your desktop. Version 2.4 ----------- Never officially released, but some people downloaded it from CVS. It once again deletes records in BBDB when deleted on Pilot. Only sets first name, last name and company on pilot if a value was given on the pilot (minor issue really). Version 2.3 ----------- Added support for bbdb file format version 6 Incorperated a fix for bbdb files containing Non-ASCII chars. Contributed by Aaron Kaplan, thanks!. Now updates timestamp, and creates creation-date as appropriate. Version 2.2 ----------- Starting with this version if a bbdb record has a note called 'pilot-ignore' the record will not be considered for syncing with your palm pilot. This version also fixes a number of small bugs and inconveniences in the previous versions. See ChangeLog for details. Version 2.1 ----------- This fixes an apparent problem with fast/slow sync detection. While I haven't seen an issue here, I did receive a bug report. Even if what I was doing wasn't wrong it was different from all the other conduits so I decided to switch methods to be more consistent. Version 2.0 ----------- Small number of cosmetic/usability improvements from 2.0b3. See ChangeLog for details. First non-beta release of 2.0. I now consider this more reliable than the 1.x series in the face of normal modifications over time (especially new/modified fields). Version 2.0b3 (Still BETA, but is my main conduit) ------------- Fixed a bug that affected the importing of new pilot addresses. Added a few more refinements to the StandaloneHost status stuff. Changed the format of the BBDB Mapping. All the pilot and bbdb fields are now quoted. This helps to ensure that bbdb 'locations' that have spaces in them are ok. Currently it will read both of them with no problem however some time in the future I plan to remove support for the old unquoted format. So I suggest forcing a full bbdb<->pilot merge (most easily accomplished by removing your 'ids' file, see below). I also added code to 'fix' pilot labels to be valid lisp identifiers (spaces -> '-', etc). This is needed because the note labels in bbdb are read as lisp identifiers. Version 2.0b2 (Currently BETA use with caution) ----------- This represents what I would consider the first fully functional implementation of a BBDB<->Pilot conduit. Major new features: 1) Multiple Pilot records for each BBDB record. By default each address in a BBDB record will be mapped to a pilot record. It will associate a phone with a 'location' that matches the address location as the primary phone for each address. If you change a phone in one pilot record it will be updated in the associated BBDB record _and_ all other pilot records that are associated with the same BBDB record (assuming they include that phone of course) during the sync operation. 2) Specification of BBDB<->Pilot field mappings. There is a new file called 'translate' that lives in the SyncBBDB directory for PilotManager (you can specify the file to use for 'bootstrap'). This file consists of name pairs one per line: This will be used when trying to see if it is appropriate to put a bbdb field into the pilot, and will be used to determine the name used in for Pilot fields. Note that generally this only applies to future syncs So don't change this file and expect everything to suddenly be updated in your Pilot. General Improvements: The code is more aggressive about trying to put bbdb fields into the pilot. This is most noticeable in conjunction with the field translation file but even with out it you will likely see more fields translated to the pilot. than previous versions. It now provides feedback as the sync proceeds (most notably when it is loading the Address database during a full sync). The mapping between a BBDB and palm record is editable. Once the initial mapping is made you can adjust it from bbdb. The complete description of the last mapping is stored in a new bbdb field 'pilot-id'. The basic format is:
[: