I’ve wanted a proper vCard library in Emacs for a while now. Email clients attach vCards to messages, phones export contact databases as vCard files, and just about every contact management system speaks the format – yet Emacs has never had a good way to work with vCard data as structured objects rather than opaque text. So I built ecard, a complete vCard 4.0 (RFC 6350) implementation in pure Emacs Lisp with no external dependencies.
Design
The library uses EIEIO to represent vCards as objects with slots for each property type from RFC 6350. Properties are objects too, carrying their group prefix, parameters, and values. This makes it straightforward to work with contact data programmatically rather than manipulating strings.
One thing that tripped me up during implementation was line folding. RFC 6350 says lines must be folded at 75 octets, not 75 characters. That’s an important distinction for UTF-8 text, where a single character can occupy several bytes. Getting it right means counting octets and folding at character boundaries so you don’t split multi-byte sequences.
Usage
Parsing a vCard file gives you an ecard object you can query directly:
(let ((contact (ecard-parse-file "~/contacts/john.vcf")))
;; Get the formatted name
(ecard-get-property-value contact 'fn)
;; Get all email addresses
(ecard-get-property-values contact 'email)
;; Get first phone number
(ecard-get-property-value contact 'tel))
Creating a new vCard works the way you’d expect:
(let ((contact (ecard-create
:fn "Jane Smith"
:email '("[email protected]" "[email protected]")
:tel "+1-555-1234"
:org "Acme Corporation")))
(ecard-write-file contact "~/contacts/jane.vcf"))
And you can modify properties after the fact:
;; Replace all email addresses
(ecard-set-property contact 'email "[email protected]")
;; Add an additional phone number
(ecard-add-property contact 'tel "+1-555-9999")
The thing that actually motivated all this was handling vCard attachments in email. I wanted to parse an attached contact, pull out the fields I cared about, and fold them into my existing workflows without copying things by hand. But once parsing and serialization were working, it turned out to be useful for more than that: converting between vCard and org-contacts or BBDB, doing bulk operations on contact databases, and round-tripping vCards with full fidelity.
Testing
I put a fair amount of effort into the test suite – over 390 tests covering RFC compliance, edge cases, and real-world scenarios. That includes round-trip fidelity, correct octet-level folding, proper escaping of special characters, and all the standard vCard 4.0 properties. There are also tests for CardDAV (RFC 6352), org-contacts compatibility, and BBDB integration. Here are the RFCs the tests are based on:
- RFC-2425
- A MIME Content-Type for Directory Information
- RFC-2426
- vCard MIME Directory Profile (vCard 3.0)
- RFC-6350
- vCard Format Specification (vCard 4.0)
- RFC-6352
- CardDAV: vCard Extensions
Backwards compatibility
The library also handles vCard 2.1 and 3.0. CardDAV servers can be configured
with a :version parameter to specify which version a server (or even a
specific addressbook) expects to receive. One limitation at the moment: it
doesn’t handle MIME encoding for binary properties like photos, which are
stored as strings for now.
Please let me know of any issues or feature requests through the GitHub issues list!