[wellylug] Moving from Eudora to Ximian

Wellington Assassin pipes at resentment.org
Tue Jan 21 12:01:47 NZDT 2003


Seems like this is a common problem... Try the script below.

chmod u+x csv2vcard.pl 
../cvs2vard.pl contacts.csv contacts.vcf 

#!/usr/bin/perl -w
#
# csv2vcard.pl
#
# Copyright (C) 2001  Ximian, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# 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, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
# 
# Authors
#   Michael MacDonald <mjmac at ximian.com>
#   Chris Lahey <clahey at ximian.com>
#
# ChangeLog:
#
# 2001-12-12  Michael MacDonald  <mjmac at ximian.com>
#   Hello World!
#
use strict;
use diagnostics;
use Text::ParseWords;

sub usage
{
  print STDERR << "--EndOfUsage";

Takes a CSV-formatted list of contacts from Outlook and attempts to
convert it into a list of VCards suitable for import into Evolution.

Usage: $0 [infile outfile]

--EndOfUsage

  exit;
}

sub is_recognized_format
{
  my $line = shift;

  # Making some assumptions here...  Prolly OK.
  return $line =~ /(First Name|Middle Name|Last Name)/; 
}

sub map_columns
{
  my $line = shift;

  my @names = parse_line(',', 0, $line);

  my $ctr = 0;
  my %fieldmap = map { $_ => $ctr++ } @names;

  return %fieldmap;
}

sub build_vcard_attr_from_def
{
  my ($def, $fields, $map) = @_;

  # Valid chars for lookup (from Outlook CSV) are
  # A-Za-z0-9_-'/
  # Valid chars for formatting of attr are
  # \s,|
  my @lookup = map { s/=0A$//; s/[^\w\s\-'\/]//; $_; } split
/[\s,]*\|[\s,]*/, $def;

  foreach my $el (@lookup) {
    unless (defined($map->{ $el })) {
      print STDERR "$el is undefined\n";
      next;
    }
    if (defined($fields->[$map->{ $el }])) {
      unless ($fields->[$map->{ $el }] =~ /(^$|0\/0\/00)/) {
        $def =~ s/$el/$fields->[$map->{ $el }]/;
      } else {
        $def =~ s/((?<=\|)\s*)?$el(\s*?(?=\|))?(=0A)?,?//;
      }
    } else {
      $def =~ s/((?<=\|)\s*)?$el(\s*?(?=\|))?(=0A)?,?//;
    }
  }
  # Get rid of field delimiters
  $def =~ s/\|//g;
  # Snip off any trailing semicolons or whitespace
  $def =~ s/[\s;]*$//;

  return $def;
}

sub build_vcard_from_line {
  my ($line, %map) = @_;
  my %vcard;

  my @fields = parse_line(',', 0, $line);

  my %vcard_def = ( FN => 'Title |First Name |Middle Name |Last Name
|Suffix',
		    N => 'Last Name| Suffix|;First Name|;Middle Name|;Title',
		   'ADR;WORK'  => 'PO Box|;Business Street 2|;Business
Street|;Business City|;Business State|;Business Postal Code|;Business
Country',
		   'LABEL;QUOTED-PRINTABLE;WORK' => 'PO Box |Business
Street=0A|Business Street 2=0A|Business City,| Business State|
Business Postal Code=0A|Business Country',
		   'TEL;WORK;VOICE' => 'Business Phone',
		   'TEL;WORK;VOICE2' => 'Business Phone 2',
		   'TEL;WORK;FAX' => 'Business Fax',
		   'TEL;WORK;COMPANY' => 'Company Main Phone',
		   'ADR;HOME'  => ';Home Street 2|;Home Street|;Home City|;Home
State|;Home Postal Code|;Home Country',
		   'LABEL;QUOTED-PRINTABLE;HOME' => 'Home Street=0A|Home Street
2=0A|Home City,| Home State| Home Postal Code=0A|Home Country',
		   'TEL;HOME;VOICE' => 'Home Phone',
		   'TEL;HOME;VOICE2' => 'Home Phone 2',
		   'TEL;HOME;FAX' => 'Home Fax',
		   'ADR;POSTAL'  => ';Other Street 2|;Other Street|;Other
City|;Other State|;Other Postal Code|;Other Country',
		   'LABEL;QUOTED-PRINTABLE;POSTAL' => 'Other Street=0A|Other Street
2=0A|Other City,| Other State| Other Postal Code=0A|Other Country',
		   'TEL;VOICE' => 'Other Phone',
		   'TEL;FAX' => 'Other Fax',
		   'TEL;CELL' => 'Mobile Phone',
		   'TEL;CAR' => 'Car Phone',
		   'TEL;PAGER' => 'Pager',
		   'TEL;PREF' => 'Primary Phone',
		   'TEL;ISDN' => 'ISDN',
		   'TEL;X-EVOLUTION-CALLBACK' => 'Callback',
		   'TEL;X-EVOLUTION-TTYTDD' => 'TTY/TDD Phone',
		   'TEL;X-EVOLUTION-TELEX' => 'Telex',
		   'TEL;X-EVOLUTION-RADIO' => 'Radio Phone',
		   'EMAIL;INTERNET' => 'E-mail Address',
		   'EMAIL;INTERNET2' => 'E-mail 2 Address',
		   'EMAIL;INTERNET3' => 'E-mail 3 Address',
		    ORG => 'Company|;Department',
		    TITLE => 'Job Title',
		    ROLE => 'Profession',
                   'X-EVOLUTION-ASSISTANT' => "Assistant's Name",
		   'TEL;X-EVOLUTION-ASSISTANT' => "Assistant's Phone",
		   'X-EVOLUTION-SPOUSE' => 'Spouse',
                   'X-EVOLUTION-ANNIVERSARY' => 'Anniversary',
                   'X-EVOLUTION-MANAGER' => "Manager's Name",
                   'X-EVOLUTION-OFFICE' => 'Office Location',
		    BDAY => 'Birthday',
		    NOTE => 'Notes',
		    FBURL => 'Internet Free Busy',
		    URL => 'Web Page',
                   );

  foreach my $key (keys(%vcard_def)) {
    my $attr = build_vcard_attr_from_def($vcard_def{ $key }, \@fields,
\%map);
    if (defined($attr)) {
      $vcard{ $key } = $attr unless ($attr =~ /^$/);
    }
  } 

  return %vcard;
}

sub print_vcard_to_fh
{
  my ($fh, %vcard) = @_;
 
  print $fh "BEGIN:VCARD\n";
  foreach my $key (keys(%vcard)) {
    # Dirty hack because Evolution's vcard stores multiple email addrs
    # with same sttribute, hence key collision.  Bleah.
    # Ugh!  Same deal for multiple phones... (eg. bus. phone)
    #
    # And finally, while we're special-casing...  Outlook exports
dates
    # differently, so munge 'em if we find 'em.
    if ($key =~ /EMAIL;INTERNET/o) {
      (my $temp = $key) =~ s/\d$//;
      print $fh "$temp:$vcard{ $key }\n";
    } elsif ($key =~ /TEL;(HOME|WORK)/o) {
      (my $temp = $key) =~ s/\d$//;
      print $fh "$temp:$vcard{ $key }\n";
    } elsif ($key =~ /(BDAY|X\-EVOLUTION\-ANNIVERSARY)/o) {
      my $temp = $vcard{ $key };
      if ($temp =~ /(\d\d)\/(\d\d)\/(\d\d)/) {
        # Y2k !!  MS Didn't learn anything.
        # Hope no one was born before 1915
        if ((1900 + $3) < 1915) {
          print $fh "$key:20$3-$1-$2\n";
        } else {
          print $fh "$key:19$3-$1-$2\n";
        }
      } else {
        # Something's funky...  Just delete the attribute
        print STDERR "Couldn't figure out what to do with $key:$vcard{
$key }\n";
        delete($vcard{ $key });
      } 
    } else {
      print $fh "$key:$vcard{ $key }\n";
    }
  }
  print $fh "END:VCARD\n\n";
}

my $in  = $ARGV[0];
my $out = $ARGV[1];

usage() unless(defined($in) && defined($out));

open (IN, $in)
  or die "Can't open($in): $!\n";

open (OUT, ">$out")
  or die "Can't open($out): $!\n";

my $linectr = 0;
my %map;

while (my $line = <IN>) {
  $line =~ s/\r//g;
  $line =~ s/\n$//;
  if ($linectr == 0) {
    $linectr++;
    usage() unless is_recognized_format($line);
    %map = map_columns($line);
  } else {
    $linectr++;
    # Thanks Chris!  My eyes are bleeding and my head hurts, but it
works!
    while ($line =~ /^(("([^"]|\n|"")*")?,)*"([^"]|\n|"")*$/) {
      my $temp = $line;
      $line = <IN>;
      $line =~ s/\r//g;
      $line =~ s/\n$//;
      $line = "$temp $line";
    }
    my %vcard = build_vcard_from_line($line, %map);
    print_vcard_to_fh(\*OUT, %vcard);
  }
}  

#print STDERR "Processed ", $linectr - 1, " lines\n";

close(IN);
close(OUT);




------------------------ Yahoo! Groups Sponsor ---------------------~-->
Flexible Keyboard is the ideal accessory for PDA users that are on the move.
http://us.click.yahoo.com/dCBVZC/WnCFAA/xGHJAA/0XFolB/TM
---------------------------------------------------------------------~->

  .-.   Wellington
  /V\   Linux
 // \\  Users       
/(   )\ Group
 ^^-^^
        http://wlug.paradise.net.nz/

To unsubscribe from this group, send an email to:
wellylug-unsubscribe at egroups.com
  

Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/ 





More information about the wellylug mailing list