0

I have vCards that were exported from the Contacts app of Mac OS X 10.8.2. From what I can tell, Apple exported the notes with RTF that was compressed with LZF and converted the compressed data to base 64 before exporting the vCard. I'm trying to restore an export but the current version (Sonoma 14.2.1) ignores the notes field of the vCard. (I sanitized the contact information but didn't touch the X-APPLE-OL-NOTE section.)

Here's an example vCard:

BEGIN:VCARD
VERSION:3.0
PRODID:-//Apple Inc.//Mac OS X 10.8.2//EN
N:;Jane;;;(Baker) Smith
FN:Jane (Baker) Smith
TEL;type=HOME;type=VOICE;type=pref:(123) 123-4567
TEL;type=CELL;type=VOICE:(321) 321-7654
item1.ADR;type=HOME;type=pref:;;123 Any Street;Any Town;XX;T1T 1T1;Canada
item1.X-ABADR:ca
UID:CZTL-07DB0A0E-002E-0202-FF32-00816
X-ABUID:6EB029FB-BBBB-4242-A054-C5F4392245BD:ABPerson
X-EVOLUTION-FILE-AS:Smith, Jane
X-APPLE-OL-MAPPING-INFO:1
X-APPLE-OL-NOTE;TYPE=text/rtf;X-COMPRESSION=X-OL;X-HASH=03c582c83054be62e05
e09df57aff23b;X-HASHALGORITHM=X-zMD5;ENCODING=B:fAAAAI0AAABMWkZ1YU/S1F8AAwF
AAHIB9wK3VgSQZC0AcGECgAqScASQdzEYMTA0AUAPc2g5OIYwAUAAwHJnbDEMAd0Q4nIRMgsDDN
A1DAEM0F0P4DYC0QoRAUBpAUB1VGxuAiBlAgBjAUBjAQu1IChBYnJhaGBhbXNlKQ9AFcA=
END:VCARD

The note in the above vCard is "(Abrahamse)".

I've attempted to read the Note with the following Swift code (the above vCard is stored on disk as test.vcf):

import Foundation
import Contacts

func readVCard(filePath: String) {
    do {
        // Read the content of the vCard file
        let vCardContent = try String(contentsOfFile: filePath, encoding: .utf8)
        
        // Parse the vCard data
        if let data = vCardContent.data(using: .utf8) {
            
            let contacts = try CNContactVCardSerialization.contacts(with: data)
            
            // Print information for each contact
            for contact in contacts {
                print("Contact Identifier: \(contact.identifier)")
                print("Given Name: \(contact.givenName)")
                
                for phoneNumber in contact.phoneNumbers {
                    print("Phone Number: \(phoneNumber.value.stringValue)")
                }
                
                print("Notes: \(contact.note)")
            }
        }
    } catch {
        print("Error reading the vCard file: \(error.localizedDescription)")
    }
}

// Example usage: replace "test.vcf" with the actual path to your vCard file
let vCardFilePath = "test.vcf"
readVCard(filePath: vCardFilePath)

The output of running the code is below -- the note information is not extracted properly:

Contact Identifier: 2035C9D6-6A76-49F4-A5ED-C2C073A517CE:ABPerson
Given Name: Jane
Phone Number: (123) 123-4567
Phone Number: (321) 321-7654
Notes: 
Program ended with exit code: 0

I used the base64 command at the terminal to decode the notes from 2 vcards and the first few bytes were (from xxd):

test.vcf: 00000000: 7c00 0000 8d00 0000 4c5a 4675 614f d2d4 |.......LZFuaO..  
second.vcf: 00000000: 5e1f 0000 4f4e 0000 4c5a 4675 59f1 0eeb ^...ON..LZFuY...

So the main overlap is 4c5a 4675, corresponding to LZFu. I wasn't able to match that up with the hints on the Identifying Compression Algorithms page.

How could I extract the note correctly?

6
  • Cross posted from stackoverflow.com/questions/77771698/… Commented Jan 8 at 16:24
  • Please be aware that code-level questions are off-topic on AD. People here can help you to understand the encoding/compression, but if you need help with the actual code, please flag for migration.
    – nohillside
    Commented Jan 8 at 16:58
  • When imported into Apple's Contacts.app, does the entry show any notes? If you know the input, then reverse engineering the output will be much easier. Commented Jan 8 at 18:06
  • No, the current version of Apple's Contacts.app doesn't show any notes on 2 different vcards I've tried with notes. I've also tried importing a Contacts.app backup file (a package with the extension .abbu) that was created in 2017 and the current version of Contacts.app doesn't bring in notes for any of the contacts. Commented Jan 9 at 4:24
  • 1
    I've updated my question to give the known value of the test.vcf note. Commented Jan 9 at 22:49

2 Answers 2

1

Experiment with CyberChef to decode from Base64 then examine the result interactively. This should be easier than writing Swift.

The decoded Base64 includes the characters LZW but seems to include a header that needs to be stripped off, see Identifying Compression Algorithms for encoding hints.

3
  • Thanks very much for that tip. I used the base64 command at the terminal to decode the notes from 2 vcards and the first few bytes were (from xxd): test vcard note: 00000000: 7c00 0000 8d00 0000 4c5a 4675 614f d2d4 |.......LZFuaO.. second vcard note: 00000000: 5e1f 0000 4f4e 0000 4c5a 4675 59f1 0eeb ^...ON..LZFuY... So the main overlap is 4c5a 4675, corresponding to LZFu. I wasn't able to match that up with the hints on the Identifying Compression Algorithms page. Commented Jan 9 at 4:26
  • @fractional_ideal please could you add this detail to your question. Then others are more likely to see it. Commented Jan 9 at 8:05
  • 1
    Thanks, I've updated it now. Commented Jan 9 at 22:49
-1

LZFu rings a bell, especially with RTF. It seems that Microsoft added this as a special version of LZ77 to compress RTF data: https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxrtfcp/65dfe2df-1b69-43fc-8ebd-21819a7463fb

1
  • How can this information be used to actually extract the note correctly, as the question asks?
    – nohillside
    Commented Jun 1 at 7:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .