The OpenWebMail vCard Data Structure

The vCard hash structure is how we pass data all around in the new addressbook.
This is really convenient because it allows fairly easy implementation of import and
export filters. As long as import filters convert data into the vCard data structure
they will work. As long as export filters convert the vcard data structure to whatever
format they are exporting, they will work. This universal target idea also creates the excellent
side-effect that as import and export are created, OWM will become an excellent
translation tool for addressbook data.
Each vCard in OWM is assigned a unique user id called X-OWM-UID. The X-OWM-UID is always
the key to the vCard data and is always in the format:

YYYYMMDD-HHMMSS-(Random 10 characters)-(Random 4 characters)

For Example:
20040909-073403-35PDGCRZE5OQ-HVLF

The imported or exported vcard_hash_structure can contain any of the following fields, but
the BEGIN, VERSION, N, and END fields are mandatory. This data example contains all of the
fields you will encounter as you process vcard data. Notice the repeated pattern of:

'PROPERTYNAME' => [
                   {
                     'TYPES' => {
                                  'SOMETYPEVALUE' => 'SOMETYPEDESCRIPTION'
                                },
                     'VALUE' => {
                                  'SOME VALUE HERE OR HASH OF KEYS/VALUES'
                                },
                     'GROUP' => {
                                  'SOME GROUP NAME'
                                }
                   }
                 ]

TYPES and GROUP are optional. VALUE is mandatory.
The TYPES values and descriptions are not made up, they are taken from the RFC.
The RFC contains a good explanation of how GROUP works. The basic idea is that PROPERTYNAMES can
be grouped so that grouped propertynames display together. My implementation recognizes grouping and
displays the grouping information, but it doesn't display grouped elements together.
For GROUP export support mabye just dump the group into a field. For GROUP import just populate
the GROUP with the value.

Here is an example complete vCard data structure:

my $vcard_hash_structure = {
          '20040909-073403-35PDGCRZE5OQ-HVLF' => {
                                                   'ADR' => [
                                                              {
                                                                'TYPES' => {
                                                                             'BASE64' => 'ENCODING',
                                                                             'DOM' => 'TYPE',
                                                                             'HOME' => 'TYPE',
                                                                             'INTL' => 'TYPE',
                                                                             'PARCEL' => 'TYPE',
                                                                             'POSTAL' => 'TYPE',
                                                                             'WORK' => 'TYPE'
                                                                           },
                                                                'VALUE' => {
                                                                             'COUNTRY' => 'Italy',
                                                                             'EXTENDEDADDRESS' => 'Apt. 20',
                                                                             'LOCALITY' => 'FakeCity',
                                                                             'POSTALCODE' => '90045',
                                                                             'POSTOFFICEADDRESS' => 'P.O. Box 123',
                                                                             'REGION' => 'California',
                                                                             'STREET' => '123 Fake Street'
                                                                           }
                                                              }
                                                            ],
                                                   'BDAY' => [
                                                               {
                                                                 'VALUE' => {
                                                                              'DAY' => 19,
                                                                              'MONTH' => 11,
                                                                              'YEAR' => 1962
                                                                            }
                                                               }
                                                             ],
                                                   'CLASS' => [
                                                                {
                                                                  'VALUE' => 'Private'
                                                                }
                                                              ],
                                                   'EMAIL' => [
                                                                {
                                                                  'GROUP' => 'email_section01',
                                                                  'TYPES' => {
                                                                               'PREF' => 'TYPE'
                                                                             },
                                                                  'VALUE' => 'fakeone@fake.com'
                                                                },
                                                                {
                                                                  'VALUE' => 'faketwo@fake.com'
                                                                }
                                                              ],
                                                   'FN' => [
                                                             {
                                                               'VALUE' => 'Mr. Joe Michael Bloggs III'
                                                             }
                                                           ],
                                                   'GEO' => [
                                                              {
                                                                'VALUE' => {
                                                                             'LATITUDE' => '32.550266',
                                                                             'LONGITUDE' => '-120.209183'
                                                                           }
                                                              }
                                                            ],
                                                   'LABEL' => [
                                                                {
                                                                  'TYPES' => {
                                                                               'BASE64' => 'ENCODING',
                                                                               'DOM' => 'TYPE',
                                                                               'HOME' => 'TYPE',
                                                                               'INTL' => 'TYPE',
                                                                               'PARCEL' => 'TYPE',
                                                                               'POSTAL' => 'TYPE',
                                                                               'WORK' => 'TYPE'
                                                                             },
                                                                  'VALUE' => 'P.O. Box 123
123 Fake Street, Apt. 20
FakeCity, California
         90045 Italy'
                                                                }
                                                              ],
                                                   'MAILER' => [
                                                                 {
                                                                   'VALUE' => 'OpenWebmail'
                                                                 }
                                                               ],
                                                   'N' => [
                                                            {
                                                              'VALUE' => {
                                                                           'ADDITIONALNAMES' => 'Michael',
                                                                           'FAMILYNAME' => 'Bloggs',
                                                                           'GIVENNAME' => 'Joe',
                                                                           'NAMEPREFIX' => 'Mr.',
                                                                           'NAMESUFFIX' => 'III'
                                                                         }
                                                            }
                                                          ],
                                                   'NAME' => [
                                                               {
                                                                 'VALUE' => 'vCard for Mr. Bloggs'
                                                               }
                                                             ],
                                                   'NICKNAME' => [
                                                                   {
                                                                     'VALUE' => 'Joey'
                                                                   }
                                                                 ],
                                                   'NOTE' => [
                                                               {
                                                                 'VALUE' => 'This is a note about this contact. This is a fake person.'
                                                               }
                                                             ],
                                                   'ORG' => [
                                                              {
                                                                'VALUE' => {
                                                                             'ORGANIZATIONALUNITS' => [
                                                                                                        'Local Division',
                                                                                                        'Worldwide Division'
                                                                                                      ],
                                                                             'ORGANIZATIONNAME' => 'Fake Company'
                                                                           }
                                                              }
                                                            ],
                                                   'PRODID' => [
                                                                 {
                                                                   'VALUE' => 'Open WebMail 2.30 20040424'
                                                                 }
                                                               ],
                                                   'REV' => [
                                                              {
                                                                'VALUE' => {
                                                                             'DAY' => 9,
                                                                             'HOUR' => 7,
                                                                             'MINUTE' => 34,
                                                                             'MONTH' => 9,
                                                                             'SECOND' => 3,
                                                                             'YEAR' => 2004
                                                                           }
                                                              }
                                                            ],
                                                   'ROLE' => [
                                                               {
                                                                 'VALUE' => 'Executive'
                                                               }
                                                             ],
                                                   'SORT-STRING' => [
                                                                      {
                                                                        'VALUE' => 'Bloggs'
                                                                      }
                                                                    ],
                                                   'SOURCE' => [
                                                                 {
                                                                   'VALUE' => 'http://www.fakeurl.com/mycard.vcf'
                                                                 }
                                                               ],
                                                   'TEL' => [
                                                              {
                                                                'TYPES' => {
                                                                             'HOME' => 'TYPE',
                                                                             'PREF' => 'TYPE',
                                                                             'VOICE' => 'TYPE'
                                                                           },
                                                                'VALUE' => '1-800-123-4567'
                                                              },
                                                              {
                                                                'TYPES' => {
                                                                             'BBS' => 'TYPE',
                                                                             'CAR' => 'TYPE',
                                                                             'CELL' => 'TYPE',
                                                                             'FAX' => 'TYPE',
                                                                             'ISDN' => 'TYPE',
                                                                             'MODEM' => 'TYPE',
                                                                             'MSG' => 'TYPE',
                                                                             'PAGER' => 'TYPE',
                                                                             'VIDEO' => 'TYPE',
                                                                             'WORK' => 'TYPE'
                                                                           },
                                                                'VALUE' => '1-800-345-6789'
                                                              }
                                                            ],
                                                   'TITLE' => [
                                                                {
                                                                  'VALUE' => 'President'
                                                                }
                                                              ],
                                                   'TZ' => [
                                                             {
                                                               'VALUE' => '-0300'
                                                             }
                                                           ],
                                                   'UID' => [
                                                              {
                                                                'VALUE' => 'f43431r4qfweq202'
                                                              }
                                                            ],
                                                   'URL' => [
                                                              {
                                                                'VALUE' => 'http://www.fakesite.com'
                                                              }
                                                            ],
                                                   'VERSION' => [
                                                                  {
                                                                    'VALUE' => '3.0'
                                                                  }
                                                                ],
                                                   'X-OWM-UID' => [
                                                                    {
                                                                      'VALUE' => '20040909-073403-35PDGCRZE5OQ-HVLF'
                                                                    }
                                                                  ]
                                                 }
        };

You can read more about the field names in the vcard_hash_structure by referencing the RFC for
vCard 2.1 (which is easy to read) at http://www.imc.org/pdi/pdiproddev.html. vCard 3.0 RFC is
hard to read and not as well organized as the 2.1 RFC. The RFC will explain the details when
the VALUE contains a hash key/value data set, as in the N propertyname.

Some things to remember as you work with the vcard_hash_structure:
  1) Each propertyname (like URL, VERSION, etc) points to an ARRAY of hashes. This is important to realize
     because it means that each propertyname can have multiple hashes in the array - meaning that each
     propertyname can store multiple instances of that propertyname as it appears in the vcard. Look at
     the EMAIL and TEL propertynames above for an example of multiple hashes in the propertyname array.
  2) To support all of these instances of data you must iterate through the data!
  3) The program automatically strips out any empty fields and validates the data, so just get it into
     the hash structure and the program will do the rest.
  4) Don't worry about the X-OWM-UID. The program will automatically generate and add one if it is left blank.
     So feel free to use a blank as your X-OWM-UID:

     '' => {
              'ADR' => [
     etcetera, etcetera, etcetera.....

     Of course, if you are importing multiple contacts you should generate an X-OWM-UID for each contact
     so you can store that contacts data in a unique key in the vcard hash structure. Code provided below.


########################## MAKE_X_OWM_UID ################################
# You will need this to make unique X-OWM-UID keys in the converted_vcard_hash_structure
# if there are more than one contacts being imported because the X-OWM-UID is the key
# to each structure of data.
sub make_x_owm_uid {
      my ($uid_sec,$uid_min,$uid_hour,$uid_mday,$uid_mon,$uid_year) = gmtime(time);
      my @chars = ( 'A' .. 'Z', 0 .. 9 );
      my $longrandomstring = join '', map { $chars[rand @chars] } 1..12;
      my $shortrandomstring = join '', map { $chars[rand @chars] } 1..4;
      my $uid = ($uid_year+1900).sprintf("%02d",($uid_mon+1)).sprintf("%02d",$uid_mday)."-".
                sprintf("%02d",$uid_hour).sprintf("%02d",$uid_min).sprintf("%02d",$uid_sec)."-".
                $longrandomstring."-".$shortrandomstring;
      return $uid;
}
####################### END MAKE_X_OWM_UID ################################


Unrelated to import/export filters, but still important info:

ALIGNMENT AND HOW THE TARGETAGENT PARAMETER WORKS
The way OpenWebMail loads a contact is important to understand because with embedded
agents (which are entire vCards inside a vCard) things could possibly get confusing. I needed
to design a way that I only had to be concerned with the card I was working on, and able to
ignore all the other cards in the vCard. This is where the alignment step comes in.
OpenWebMail loads the complete_vcard in memory. This is all the vcard data of the requested
contact, including all embedded agent data. Then it looks at the targetagent request and makes
the %contact hash a reference to the specific card we want to work with. Now, we only have to deal with
%contact and never have to worry about the full card. It also standardizes the steps needed to
process a card because we are always processing just one target vcard data structure at a time.
This is easier to understand visually:



The alignment step is clearly labeled when it occurs in the code of openwebmail-abook.pl.
Actually, there are many useful comments in openwebmail-abook.pl, so I recommend reading
there too.

Thats all for now!