<?xml version="1.0" encoding="windows-1252"?>
<node id="598865" title="Adding Outlook Contacts In Perl" created="2007-02-07 14:42:32" updated="2007-02-07 09:42:32">
<type id="120">
perlmeditation</type>
<author id="79942">
jgamble</author>
<data>
<field name="doctext">
&lt;p&gt;I had a emergency call to transfer address book contents from a client's old system to Microsoft Outlook.  At first this seemed simple.  The old system exported its contents in a well-formed VCF file, each &lt;a href='http://www.silicon-press.com/briefs/brief.vcard/'&gt;vCard&lt;/a&gt; separated by a double newline. But Outlook assumes that each VCF file holds only one &lt;a href='http://en.wikipedia.org/wiki/VCard'&gt;vCard&lt;/a&gt;, and imports only the first entry, skipping the rest.&lt;/p&gt;

&lt;p&gt;That's simple (I thought).  Set $/ to "\n\n", slurp in the file to an array variable, and write out each record as an individual file in a subdirectory for each user.&lt;/p&gt;

&lt;p&gt;Oops.  Not so simple.  Outlook doesn't do batch reads of VCF files.  Someone would have to click on over a thousand VCF files to enter them into Outlook, and since that someone would be me it was vitally important to come up with a different solution.&lt;/p&gt;
&lt;readmore&gt;
&lt;p&gt;A search through Google showed this to be a problem that almost no one had bothered to solve.  But I had written an &lt;a href='http://www.perlmonks.org/?node_id=128957'&gt;Outlook e-mail&lt;/a&gt; subroutine once, and thought it could be modified to handle contact items instead of mail items.&lt;/p&gt;

&lt;p&gt;Here's the outer loop around the new subroutine.  At this point the records have been slurped into the array @vrecs.  I ignored groups, which were done in an idiosyncratic way in the old system.  Some vcard items cross multiple lines, so I temporarily join them.  Then I split the entries by newlines, and created a hash to feed to outlook_vcard().&lt;/p&gt;
&lt;code&gt;
   #
   # Create a contact for each record.
   #
   foreach my $vcf (@vrecs)
   {
      my %track;
      unless ($vcf =~ /^X-GWTYPE:GROUP(END)?/)   # Skip group records.
      {
         chomp $vcf;                             # $/ is still "\n\n".
         $vcf =~ s/=0A=\n/=0A= /g;
         my @lines = split(/\n/, $vcf);

         foreach my $line (@lines)
         {
            my @parts = split(/:/, $line);
            $parts[1] =~ s/=0A= /\n/g;
            $track{$parts[0]} = $parts[1];
         }
      }
      outlook_vcard(%track);
   }
&lt;/code&gt;
&lt;p&gt;Since the outlook_vcard function was written to handle a specific problem, not all possible VFC entries are dealt with.  For example, a quick search of the client's files showed that only one e-mail address per entry was present, so I didn't have to worry about dealing with multiple e-mail addresses, something that wouldn't be true in a more generic application.&lt;/p&gt;
&lt;code&gt;
sub outlook_vcard
{
   my(%vcard_props) = @_;

   my $mail = new Win32::OLE('Outlook.Application');
   my $item = $mail-&gt;CreateItem(2);  # 2 = contact item.

   unless ($item)
   {
      warn "Outlook is not running, cannot save vCard.\n";
      return 0;
   }

   $item-&gt;{FullName} = $vcard_props{'FN'} || $vcard_props{'N'} || ' ';
   $item-&gt;{Company} = $vcard_props{'ORG'} || ' ';

   $item-&gt;{Email1Address} = $vcard_props{'EMAIL;WORK;PREF'} ||
         $vcard_props{'EMAIL;WORK;PREF;NGW'} || ' ';

   $item-&gt;{HomeAddress} = $vcard_props{'ADR;DOM;HOME;PARCEL;POSTAL'} || 
         $vcard_props{'LABEL;DOM;HOME;PARCEL;POSTAL;ENCODING=QUOTED-PRINTABLE'} || ' ';
   $item-&gt;{BusinessAddress} = $vcard_props{'ADR;DOM;WORK;PARCEL;POSTAL'} ||
         $vcard_props{'LABEL;DOM;WORK;PARCEL;POSTAL;ENCODING=QUOTED-PRINTABLE'} || ' ';
   $item-&gt;{OtherAddress} = $vcard_props{'ADR;INTL;WORK;PARCEL;POSTAL'} ||
         $vcard_props{'LABEL;INTL;WORK;PARCEL;POSTAL;ENCODING=QUOTED-PRINTABLE'} || ' ';

   $item-&gt;{BusinessTelephoneNumber} = $vcard_props{'TEL;WORK'} || ' ';
   $item-&gt;{MobileTelephoneNumber} = $vcard_props{'TEL;CELL'} || ' ';
   $item-&gt;{HomeTelephoneNumber} = $vcard_props{'TEL;HOME'} || ' ';
   $item-&gt;{PrimaryTelephoneNumber} = $vcard_props{'TEL;PREF'} || ' ';
   $item-&gt;{BusinessFaxNumber} = $vcard_props{'TEL;PREF;FAX'} || ' ';

   $item-&gt;{Title} = $vcard_props{'TITLE'} || ' ';
   $item-&gt;{Body} = $vcard_props{'NOTE'} ||
          $vcard_props{'NOTE;QUOTED-PRINTABLE'} || ' ';

   $item-&gt;Save();
   return 1;
}
&lt;/code&gt;
&lt;/readmore&gt;
&lt;hr&gt;
&lt;p&gt;
&lt;ol&gt;&lt;li&gt;&lt;a href='http://www.microsoft.com/technet/scriptcenter/resources/officetips/apr05/tips0426.mspx'&gt;Create a contact in Outlook using VB&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href='http://www.dicks-clicks.com/excel/olContacts.htm#Create_a_Contact'&gt;Another article on the same subject&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href='http://msdn2.microsoft.com/en-us/library/aa210907(office.11).aspx'&gt;The properties of ContactItem&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;/p&gt;
</field>
</data>
</node>
