Copying Contacts from Your Nokia S40 Phone to Google and Android

Introduction

Like millions of others around the world, I have abandoned Nokia’s smartphones in favour of the slick, open, connected and affordable Android devices – although I still have one of Nokia’s cheapest handsets for backup. The cheapo S40 Nokia has superb battery life and takes 2 SIMs so when the smartphone’s out of juice I can still have home and business lines in action.

Before reading further, I have made a handy Android app that makes all this much easier – you can get it on Google Play here.

However, I did run into some hassle in migrating hundreds of contacts from my Nokia feature phone to the Android world. The Nokia C2-00 allows me to backup to the flash card but, on the face of it, the .nbf backup file produced is a proprietary format. No fear, it’s just a zip file renamed with a weird extension. So, rename and open in your archive tool to find, happily, a whole load of open-format .vcf vCard files, one for each contact in the handset. Great! GMail Contacts allows import in vCard format! Great!

There’s a Perl Script For That…

But wait, I have over 300 contacts to import, do I really have to go through the pointy-clicky GMail Contacts import UI over 300 times? Sounds boring and error prone. So I wrote a script:

#!/usr/bin/perl
################################################################################
#
# Tool to help migrate from a Nokia handset to a Android handset / Google 
# contacts.
#
# Marcus Jenkins - January 2012.
# 
# Legal disclaimer necessary for those sad people make a living from
# pointless litigation:
# No warranty!  If this breaks anything, it isn't my fault.  
# Use at your own risk.
#
# You will need the Text::vCard::Addressbook Perl module that you 
# can get from www.cpan.org.
#
# In your Nokia S/40 phone, go to Settings / Backup and backup all the contacts
# to a memory card.  This will create an NBF file that you can get onto your
# PC, e.g. by plugging a USB cable into the phone.  This is how I did it with
# my Nokia C2-00.  The NBF file is a zip format file, so you can just rename
# the file so that it has a .zip extension and then extract all the .vcf files
# to a temporary directory.  Gmail has a facility at the time of writing to 
# import one vCard / vcf file at a time - I don't fancy clicking around for 
# ~300 vcf files.  So...
#
# Then use this script: 
#
#  perl convertNokiaBackupToGoogleImport.pl  
#
#  e.g. perl  convertNokiaBackupToGoogleImport.pl contacts contacts.csv
#
# The generated CSV file can then be hand-tweaked if necessary and then
# imported using the GMail web user interface.  Then the contacts should
# be automagically available in your Android phones thanks to the brillant
# Google / Android ecosystem.
#
# I used another Perl script to post-filter the CSV file to auto-add country 
# code prefrixes, etc., but that logic is fairly well specific to my data set
# so suggest you roll your own or use a spreadsheet app to post-filter prior to
# importing to GMail.
#
# Good luck.
#
################################################################################

use strict;
use Text::vCard::Addressbook;

die if scalar @ARGV != 2;

my $dir = @ARGV[0];
my $outFile = @ARGV[1];

$dir =~ s//$//;

print "Scanning vcf contact files in $dir to $outFile...n";

my $filePattern = "$dir/*vcf";
my @files = glob($filePattern);
my @contacts;
my $maxDefinedPhoneNumbersForAContact;

foreach my $file(@files){

  my $adddressBook = Text::vCard::Addressbook->load([$file]);

  foreach my $vcard ($adddressBook->vcards()) {
    
    my $rNames = $vcard->get({ 'node_type' => 'N' });
    my $nameNode = $rNames->[0];
    my $firstName = $nameNode->given();
    my $surname = $nameNode->family();
    
    my $rTelephoneNumbers = $vcard->get({ 'node_type' => 'TEL' });

    my @phoneNumbersForCSV;    
    foreach my $telephoneNumber (@$rTelephoneNumbers) {
      
      my $rTypes = $telephoneNumber->types();
      
      my $voiceFlagDefined = 0;
      my $specificPhoneTypeDefined = 0;;
      foreach my $type (@$rTypes) {
        
        if($type eq 'work') {
         
          push @phoneNumbersForCSV, {type => 'Business', 
            number => $telephoneNumber->value()}; 
          $specificPhoneTypeDefined = 1;
        }
        elsif($type eq 'home') {
         
          push @phoneNumbersForCSV, {type => 'Home',
            number => $telephoneNumber->value()}; 
          $specificPhoneTypeDefined = 1;
        }
        elsif($type eq 'mobile' || $type eq 'cell') {
         
          push @phoneNumbersForCSV, {type => 'Mobile',
            number => $telephoneNumber->value()}; 
          $specificPhoneTypeDefined = 1;
        }
        elsif($type eq 'pref' || $type eq '8bit') { 

          # Ignore        
        }
        elsif($type eq 'voice') {
          
          $voiceFlagDefined = 1;
        }
        else {
          
          print "Unknown type $typen";
        }
      }
      if($voiceFlagDefined && !$specificPhoneTypeDefined) {
      
        push @phoneNumbersForCSV, {type => 'Home',
          number => $telephoneNumber->value()}; 
      }
    }
    
    # Update counts for CSV column definition header
    if(scalar @phoneNumbersForCSV > $maxDefinedPhoneNumbersForAContact) {
      
      $maxDefinedPhoneNumbersForAContact = 
        scalar @phoneNumbersForCSV;
    }
    
    # Full name needed for sorting and also for Google import
    my $fullName = $firstName;
    if(length($surname)) {
        
      if(length($fullName)) {
      
        $fullName .= ' ';  
      } 
      $fullName .= $surname;
    }

    
    my %newContact = (firstName => $firstName, surname => $surname, 
      fullName => $fullName, phoneNumbers => @phoneNumbersForCSV);
    
    push @contacts, %newContact;
  }
}

# Write the CSV file, sorted in first name, last name format where last name
# takes place of the first name if the first name wasn't defined in the Nokia
# handset - just like in the Nokia handset (handy for post-filtering)
open FHOUT, ">$outFile";
print FHOUT 'Name,Given Name,Additional Name,Family Name,Yomi Name,' .
  'Given Name Yomi,Additional Name Yomi,Family Name Yomi,Name Prefix,' .
  'Name Suffix,Initials,Nickname,Short Name,Maiden Name,Birthday,Gender,' .
  'Location,Billing Information,Directory Server,Mileage,Occupation,' .
  'Hobby,Sensitivity,Priority,Subject,Notes,Group Membership';
for (my $i = 1; $i <= $maxDefinedPhoneNumbersForAContact; $i ++) {
     
  print FHOUT ",Phone $i - Type,Phone $i - Value";
}
print FHOUT "n";

foreach my $contact (sort sortContactName @contacts) {
  
  print FHOUT 
    "$contact->{fullName},$contact->{firstName},,$contact->{surname}," .
    ',,,,,,,,,,,,,,,,,,,,,,';

  # print the phone number type / value pairs for this contact
  for (my $i = 0; $i < $maxDefinedPhoneNumbersForAContact; $i ++) {
    
    my $rPhoneListing = @{ $contact->{phoneNumbers} }[$i];
    my $phoneNumberType;
    my $phoneNumber;
    if(defined($rPhoneListing)) {
      
      $phoneNumberType = $rPhoneListing->{type};
      $phoneNumber = $rPhoneListing->{number};
    }
       
    print FHOUT ",$phoneNumberType,$phoneNumber";
  }

  print FHOUT "n";
}
close FHOUT;

###############################################################################
sub sortContactName {
  
  my $rContact1 = $a;
  my $rContact2 = $b;
  my $compare1 = $rContact1->{fullName};
  my $compare2 = $rContact2->{fullName};
  
  
  return $compare1 cmp $compare2;
}