#!/usr/bin/perl use lib "/folks/arensb/proj/palm-perl"; use Palm::Address; $KABFILE = "$ENV{HOME}/.kde/share/apps/kab/addressbook.database"; $SIG{"__DIE__"} = \&Die; $SIG{"__WARN__"} = \&Warn; # Parse command-line arguments if ($ARGV[0] ne "conduit") { die "402 Missing or bad conduit argument\n"; } # Make sure there's a flavor argument if (!defined($ARGV[1])) { die "403 Missing flavor argument\n"; } # Make sure the flavor is supported if (lc($ARGV[1]) ne "fetch") { die "404 Unsupported flavor: [$ARGV[1]]\n"; } # Read the conduit fields from stdin %HEADERS = (); while () { my $header; # Header field my $value; # Header field's value chomp; last if /^$/; # Blank line is end of headers if (!/^(\w+):\s(.*)/) { die "405 Invalid input\n"; } $header = $1; $value = $2; $HEADERS{$header} = $value; } if (!defined($HEADERS{"OutputDB"})) { die "406 Missing header field: OutputDB\n"; } # Read the existing file, if it exists. Otherwise, create a new one. if ( -f $HEADERS{"OutputDB"} ) { $pdb = new Palm::PDB; $pdb->Load($HEADERS{"OutputDB"}); # XXX - What to do if it's the wrong type/creator? } else { $pdb = new Palm::Address; } # Load kab file open KAB, "< $KABFILE" or do { # Don't touch the PDB print STDOUT "302 No kab file\n"; exit 0; }; # Skip everything up to the beginning of the actual records. while () { chomp; last if /^\s*\[Entries\]/; } # Read one record at a time. while () { chomp; next if /^\s*#.*/; # Ignore comments next if /^\s*$/; # and blank lines last if /^\s*\[END Entries\]/; if (!/^\s*\[(\d+)\]/) { warn "301 Invalid line: \"$_\"\n"; next; } $kabrec = $1; my %fields = (); while () { next if /^\s*#.*/; # Ignore comments next if /^\s*$/; # and blank lines last if /^\s*\[END $kabrec\]/; if (/^\s*([-\w]+)=\"([^\"]*)\"/) { my $key = $1; my $value = $2; $value =~ s/\\n/\n/g; $fields{$key} = $value; } } # Got a record. Add it to the PDB or modify it, as necessary. &add_kab_record($pdb, \%fields); } close KAB; # For each Palm record that's not in the kab file, delete and expunge it foreach $record (@{$pdb->{"records"}}) { next if $record->{"_seen"}; $pdb->delete_Record($record, 1); } $pdb->Write($HEADERS{"OutputDB"}); print STDOUT "201 Success\n"; exit 0; # add_kab_record # Takes a PDB structure and a hash describing a kab record. Adds the # record to the file. sub add_kab_record { my $pdb = shift; my $kabrec = shift; my $record; # Find the PDB record that corresponds to this kab record. If # it doesn't exist, create it and add it to the PDB. $record = $pdb->findRecordByID($kabrec->{"X-PilotID"}) || $pdb->append_Record; $record->{"_seen"} = 1; # Internal marker indicating that we've # dealt with this record. if ($record->{"fields"}{"name"} ne $kabrec->{"name"}) { $record->{"fields"}{"name"} = $kabrec->{"name"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"firstName"} ne $kabrec->{"firstname"}) { $record->{"fields"}{"firstName"} = $kabrec->{"firstname"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"company"} ne $kabrec->{"org"}) { $record->{"fields"}{"company"} = $kabrec->{"org"}; $record->{"attributes"}{"dirty"} = 1; } # XXX - Should set "company" to "org \n orgUnit \norgSubUnit". if ($record->{"fields"}{"address"} ne $kabrec->{"address"}) { $record->{"fields"}{"address"} = $kabrec->{"address"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"city"} ne $kabrec->{"town"}) { $record->{"fields"}{"city"} = $kabrec->{"town"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"state"} ne $kabrec->{"state"}) { $record->{"fields"}{"state"} = $kabrec->{"state"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"zipCode"} ne $kabrec->{"zip"}) { $record->{"fields"}{"zipCode"} = $kabrec->{"zip"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"country"} ne $kabrec->{"country"}) { $record->{"fields"}{"country"} = $kabrec->{"country"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"title"} ne $kabrec->{"role"}) { $record->{"fields"}{"title"} = $kabrec->{"role"}; $record->{"attributes"}{"dirty"} = 1; } if ($record->{"fields"}{"note"} ne $kabrec->{"comment"}) { $record->{"fields"}{"note"} = $kabrec->{"comment"}; $record->{"attributes"}{"dirty"} = 1; } # For each of the custom fields, see if there's a field # in $kabrec by the same name. If so, set it. Otherwise, # ignore it. foreach $custom ( qw( custom1 custom2 custom3 custom4 ) ) { my $fieldname; $fieldname = $pdb->{"appinfo"}{"fieldLabels"}{$custom}; $fieldname =~ s/\W+/-/g; # Replace bogus chars if (exists($kabrec->{$fieldname}) && ($record->{$custom} ne $kabrec->{$fieldname})) { $record->{$custom} = $kabrec->{$fieldname}; $record->{"attributes"}{"dirty"} = 1; } } # Due to the clever, flexible, pain-in-the-ass way that phone # numbers and email addresses are implemented in AddressBook, # and due to the limited number of such fields in 'kab', # there's no one-to-one correspondence between fields, as with # "firstname" and "firstName", above. We have to look through # each of the "phone*" fields in the record, see if it's of # the correct type, and whether it it contains the phone # number. my @kabphones = split /\n/, $kabrec->{"telephone"}; my @kabemails = split /\\e/, $kabrec->{"emails"}; my @kabfax = split /\n/, $kabrec->{"fax"}; my $number; number: foreach $number (@kabfax) { foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { if (($record->{"phoneLabel"}{$field} == 2) && ($record->{"fields"}{$field} =~ /^\Q$number\E$/m)) { # Found $number in a "Fax" field. All is well next number; } if (($record->{"phoneLabel"}{$field} == 3) && ($record->{"fields"}{$field} =~ /^\Q$number\E\s+\(fax\)$/m)) { # Found "$number (fax)" in an "Other" field. # All is well. next number; } } # $number wasn't found in any phone field. See if it's # in the "Note" field. if ($record->{"fields"}{"note"} =~ /^\Q$number\E\s+\(fax\)$/m) { # Found it in the "note" field. All is well. next number; } # $number wasn't found anywhere. # Try to: # 1 - Find a "Fax" field and add $number # 2 - Find an empty field, make it "Fax" and add $number # 3 - Find an "Other" field and add "$number (fax)" # 4 - Add "$number (fax)" to the note field # XXX - Fix 'kab-dump' to look for these as well # First attempt: find an existing "Fax" field and add # $number foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { next unless $record->{"phoneLabel"}{$field} == 2; # Only consider fax entries # This is a fax field. if ($record->{"fields"}{$field} eq "") { $record->{"fields"}{$field} = $number; } else { $record->{"fields"}{$field} .= "\n" . $number; } $record->{"attributes"}{"dirty"} = 1; next number; } # Second attempt: find an empty field, make it a "Fax" # field and add $number. foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { next unless $record->{"fields"}{$field} =~ /^\s*$/s; # Found an empty field. Add $number, and mark # this as being a fax number. $record->{"phoneLabel"}{$field} = 2; $record->{"fields"}{$field} = $number; $record->{"attributes"}{"dirty"} = 1; next number; } # Third attempt: find an "Other" field and add # "$number (fax)" foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { next unless $record->{"phoneLabel"}{$field} == 3; # Only consider "Other" entries # This is an "Other" field. if ($record->{"fields"}{$field} eq "") { $record->{"fields"}{$field} = $number . " (fax)"; } else { $record->{"fields"}{$field} .= "\n" . $number . " (fax)"; } $record->{"attributes"}{"dirty"} = 1; next number; } # Fourth and final attempt: add "$number (fax)" to the # note field. if (($record->{"fields"}{"note"} ne "") && ($record->{"fields"}{"note"} !~ /\n$/)) { # Make sure the fax number is on a separate line $record->{"fields"}{"note"} .= "\n"; } $record->{"fields"}{"note"} .= $number . " (fax)"; $record->{"attributes"}{"dirty"} = 1; } my $address; address: foreach $address (@kabemails) { foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { if (($record->{"phoneLabel"}{$field} == 4) && ($record->{"fields"}{$field} =~ /^\Q$address\E$/m)) { # Found $address in an "E-mail" field. # All is well next address; } if (($record->{"phoneLabel"}{$field} == 3) && ($record->{"fields"}{$field} =~ /^\Q$address\E$/m)) { # Found $address in an "Other" field. # All is well. next address; } } # $address wasn't found in any phone field. See if it's # in the "Note" field. if ($record->{"fields"}{"note"} =~ /^\Q$address\E$/m) { # Found it in the "note" field. All is well. next address; } # $address wasn't found anywhere. # Try to: # 1 - Find an "E-mail" field and add $address # 2 - Find an empty field, make it "E-mail" and add $address # 3 - Find an "Other" field and add $address # 4 - Add $address to the note field # XXX - Fix 'kab-dump' to look for these as well # First attempt: find an existing "E-mail" field and # add $address foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { next unless $record->{"phoneLabel"}{$field} == 4; # Only consider E-mail entries # This is an E-mail field. if ($record->{"fields"}{$field} eq "") { $record->{"fields"}{$field} = $address; } else { $record->{"fields"}{$field} .= "\n" . $address; } $record->{"attributes"}{"dirty"} = 1; next address; } # Second attempt: find an empty field, make it an # "E-mail" field and add $address. foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { next unless $record->{"fields"}{$field} =~ /^\s*$/s; # Found an empty field. Add $address, and mark # this as being an E-mail field $record->{"phoneLabel"}{$field} = 4; $record->{"fields"}{$field} = $address; $record->{"attributes"}{"dirty"} = 1; next address; } # Third attempt: find an "Other" field and add # $address foreach $field ( qw( phone1 phone2 phone3 phone4 phone5 ) ) { next unless $record->{"phoneLabel"}{$field} == 3; # Only consider "Other" entries # This is an "Other" field. if ($record->{"fields"}{$field} eq "") { $record->{"fields"}{$field} = $address; } else { $record->{"fields"}{$field} .= "\n" . $address; } $record->{"attributes"}{"dirty"} = 1; next address; } # Fourth and final attempt: add $address to the # note field. if (($record->{"fields"}{"note"} ne "") && ($record->{"fields"}{"note"} !~ /\n$/)) { # Make sure the address is on a separate line $record->{"fields"}{"note"} .= "\n"; } $record->{"fields"}{"note"} .= $address; $record->{"attributes"}{"dirty"} = 1; } # XXX - Same thing for the phone numbers # XXX - Go through the phone numbers and addresses in $record # and remove any that aren't in $kabrec. }