Why I’m not working on vCard validators

The vCard 3.0 validator is ten years old. The standard has moved on, Python has moved on, and I’d like to think I’ve learned a thing or two as a developer. But I’m not working on any vCard validators, and probably won’t be for the foreseeable future.

Maybe I’m suffering from second-system syndrome, but I’ve tried to reboot the project at least three times now. The Python code isn’t very elegant, fast or well tested. I set up skeletons in Java version and Rust, both excellent languages. I tried defining an ANTLR grammar. But I’ve got nothing to show for any of that. The main problem is motivation: vCard 3, 4 and jCard are incredibly out of date, to the point where writing a parser is equal parts painful and boring.

standards

And now, because the Internet needs more opinions (and standards): a human friendly address card format is possible. vCard/jCard is not it. When someone brave and tenacious enough to take this on comes around, I really hope they take at least the following into account:

  • Use a popular, existing serialization format. JSON won in this space. It’s not perfect, but it’s here to stay and it’s a good compromise between human and machine readable.
  • Don’t create another jCard. If you were developing the world’s first address card format, what would users and developers want to see?
  • Conversely, vCard did many things right. UTF-8, base64 and ISO dates are definitely good things.
  • All caps is hard to read. Don’t shout.
  • Don’t limit (or even recommend) a line length or line splitting scheme. Address cards shouldn’t be inlined into emails or documents any more than CSV files.
  • Dictionaries (as in associated key/value pairs) are useful. Use them.
  • Lists are useful. Use them.
  • Preferred name should probably be the only required name field.
  • Time zones are not UTC offsets. I do not live in time zone “+12:00”, because that offset changes twice per year. I live in time zone “Pacific/Auckland”. This can be the difference between being on time and wasting everybody’s time.
  • Allow linking to photos. Sure URLs are ephemeral, but so is basically everything in an address card.
  • Any property not in the standard should simply be ignored, as long as the entire document is still a valid instance of the serialization format.
  • Internationalization and localization are complex but important.

What will such a standard look like? Maybe something like this will be possible:

{
    "preferred name": "Jane Doe",
    "phone": {
        "cell": ["+99 9999-9999", "+11 0000 0000"],
        "home": "+99 9999-9999"
    },
    "URL": {
        "blog": "https://…",
        "work": ["https://example.org/", "https://example.com/"]
    },
    "photo": "https://…",
    "my custom property": "…",
    "time zone": "Pacific/Auckland"
}

vCard 3.0 validator and parser

Did you know that even the vCards listed in the official RFC are not valid? It clearly says The vCard object MUST contain the FN, N and VERSION types. Still, the example vCards are both clearly missing the N type. As somebody else remarked, releasing a format spec without some reference validator is bound to result in all sorts of invalid implementations.

After searching for a vCard validator without success, I’ve therefore started my own vCard module in Python. It tries to create an object with all the information from a vCard string, and returns what I hope are useful error and warning messages if there’s anything wrong.

Update: Added file validation – Now you can validate files with several vCards from the command line.

Install / upgrade:
sudo pip install --upgrade vcard

Validate vCard files:
vcard *.vcf

HOWTO: Synchronizing vCard address books with Nokia 6300 via Bluetooth on Ubuntu Linux

Edit: After learning a bit of sed magic, this is now a single script.

This Works For Me™, and I hope it works with other phones and on other distributions as well, but no guarantees.

Here’s how:

  1. Download synchronize-bluetooth.sh.
  2. chmod u+x synchronize-bluetooth.sh
  3. sudo apt-get install gnokii
  4. Setup Gnokii (replace the phone name): echo -e "[global]\nmodel = 6510\nport = $(hcitool scan | grep 'phone name' | cut --fields=2)\nconnection = bluetooth" > ~/.gnokiirc
  5. Turn Bluetooth on on the computer and the phone.
  6. Pair the computer and phone. On Ubuntu, simply click the Bluetooth icon and follow the "Set up new device..." wizard.
  • If you want to copy your existing contacts to disk, you can run ./synchronize-bluetooth.sh -r > phone.vcf
  • To copy vCards to the phone, overwriting the existing contacts, run cat *.vcf | ./synchronize-bluetooth.sh -f
  • Note that synchronize-bluetooth.sh works only with the internal phone memory. Address book entries stored in the SIM card, phone log or elsewhere will not be read or written.

    Some features of the script:

    • Works with plain /bin/sh, so it should work on any Linux / BSD distribution.
    • Has separate parameters to read, write and flush contacts.

    synchronize-bluetooth.sh

    #!/bin/sh
    #
    # NAME
    #    synchronize-bluetooth.sh - Synchronize contacts with Bluetooth device.
    #
    # SYNOPSIS
    #    synchronize-bluetooth.sh [options]
    #
    # OPTIONS
    #    -f,--flush       Flush entries on the phone before synchronization
    #    -r,--read        Read phonebook entries (no synchronization)
    #
    # EXAMPLES
    #    ./synchronize-bluetooth.sh -r
    #        Read phone address book entries.
    #
    #    cat *.vcf | ./synchronize-bluetooth.sh -fv
    #        Remove all phone address book entries and write new ones.
    #
    # DESCRIPTION
    #    Warning: Overwrites the existing entries on the phone if you give it any
    #    input.
    #
    #    Some limitations:
    #    - Gnokii (or the phone) doesn't understand N, NICKNAME, ORG and URL
    #      parameters.
    #    - Conflicts have to be resolved manually.
    #    - Getting a "Data format is not valid" error for unknown reasons.
    #    - Needs conv.pl so long as Gnokii / phone doesn't understand the full vCard
    #      spec.
    #
    #    Tested on Nokia 6300 under Ubuntu (7.10-9.10). Instructions:
    #    <https://l0b0.wordpress.com/2008/03/18/howto-copying-vcard-address-books-to-nokia-6300-via-bluetooth-on-ubuntu-linux/>
    #
    # BUGS
    #    1: Not yet tested on other phones or operating systems.
    #    2: No automated comparison of computer and phone entries.
    #    3: Should use safe temporary directory creation - See web pages.
    #    4: Flushing doesn't always get rid of all address book entries.
    #
    #    Email bugs to victor dot engmark at gmail dot com. Please include the
    #    output of running this script.
    #
    # COPYRIGHT AND LICENSE
    #    Copyright (C) 2008-2010 Victor Engmark
    #
    #    This program is free software: you can redistribute it and/or modify
    #    it under the terms of the GNU General Public License as published by
    #    the Free Software Foundation, either version 3 of the License, or
    #    (at your option) any later version.
    #
    #    This program is distributed in the hope that it will be useful,
    #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #    GNU General Public License for more details.
    #
    #    You should have received a copy of the GNU General Public License
    #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    #
    ################################################################################
    
    # Init
    PATH='/usr/bin:/bin'
    ifs_original="$IFS" # Reset when done
    cmdname=$(basename $0)
    directory=$(dirname $0)
    
    # Exit codes from /usr/include/sysexits.h, as recommended by
    # http://www.faqs.org/docs/abs/HTML/exitcodes.html
    EX_USAGE=64
    
    # Output error message with optional error code
    error()
    {
        test -t 1 && {
            tput setf 4
            echo "$1" >&2
            tput setf 7
        } || echo "$1" >&2
        if [ -z "$2" ]
        then
            exit $EX_UNKNOWN
        else
            exit $2
        fi
    }
    
    usage()
    {
        # Print documentation until the first empty line
        while read line
        do
            if [ ! "$line" ]
            then
                exit $EX_USAGE
            fi
            echo "$line"
        done < $0
    }
    
    # Process parameters
    params=$(getopt -o fr -l flush,read --name $cmdname -- "$@")
    if [ $? -ne 0 ]
    then
        usage
    fi
    
    eval set -- "$params"
    
    while true
    do
        case $1 in
            -f|--flush)
                flush=1
                shift
                ;;
            -r|--read)
                read=1
                shift
                ;;
            --) shift
                break
                ;;
            *)
                usage
                ;;
        esac
    done
    
    if [ $read ]
    then
        gnokii --getphonebook ME 1 end --vcard \
            || error 'Failed to get phonebook.'
        exit
    fi
    
    if [ $flush ]
    then
        gnokii --deletephonebook ME 1 end \
            || error 'Could not flush phone entries.'
    fi
    
    sleep 1
    
    # Is there anything on stdin?
    if [ -t 0 ]
    then
        exit
    fi
    
    sed -n '1h;1!H;${;g;s/\r\n //g;p}' <&0 | \
    sed -E s/^ADR\;.*?\(home\|work\).*?:/ADR\;TYPE=\\1:/i\;s/^EMAIL\;.*?\(INTERNET\).*?:/EMAIL\;TYPE=\\1:/i\;s/^TEL\;.*?\(CELL\|FAX\|HOME\|WORK\).*?:/TEL\;TYPE=\\1:/i | \
    gnokii --writephonebook --overwrite --memory-type ME --vcard
    
    err_code=$?
    if [ $err_code -eq 20 ]
    then
        echo 'Data format error, eh? Dunno what to do about that for now.'
    elif [ $err_code -ne 0 ]
    then
        error 'Could not write to phone.' $err_code
    fi

    If you have problems sending the vCards to your phone, you may want to validate them first.