123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- #!/usr/bin/perl -w
- use strict;
- use Getopt::Long qw(:config pass_through);
- use Text::CSV_XS;
- # * This file 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, write to the Free Software
- # * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- # *
- # * Copyright 2007/2008 Paul Mangan <paul@claws-mail.org>
- # *
- #
- # Import CSV exported address books to Claws Mail
- # Supported address books:
- # Becky >= 2.41
- # Thunderbird >= 2.0.0.6
- # Kmail >= 1.9.7 / Kaddressbook >= 3.5.7
- # ** kmail bug: can export badly formatted csv **
- # Gmail
- # Fox Mail
- #
- # Becky: full export with titles
- # thunderbird: export as 'comma separated'
- # kmail/kaddressbook: Export CSV list
- # gmail: export Outlook format
- # foxmail: export with all possible headers
- # basic: a csv file only containing these fields 'First Name', 'Last Name', 'Nickname', 'Email Address'
- ###
- my $quote_char = '"';
- my $esc_char = '"';
- my $sep_char = ',';
- ###
- my $script = "csv2addressbook.pl";
- my $type = '';
- my $csvfile = '';
- my $bookname = '';
- my $iNeedHelp = '';
- my $known_types = qr/^(?:becky|thunderbird|kmail|gmail|foxmail|basic)$/;
- GetOptions("type=s" => \$type,
- "csv=s" => \$csvfile,
- "name=s" => \$bookname,
- "help" => \$iNeedHelp);
- my @becky_fields = ('Name','E-mail Address', 'Nickname (Input shortcut)',
- 'Web Page','Notes','Company','Department','Job Title',
- 'Job Role','Last Name','First Name','Middle Name',
- 'Birthday','Home Phone','Business Phone','Mobile Phone',
- 'Fax','Street','City','State','Postal Code','Country',
- 'Delivery Label');
- my @tbird_fields = ('First Name','Last Name','Display Name','Nickname',
- 'Primary Email','Secondary Email','Work Phone',
- 'Home Phone','Fax Number','Pager Number','Mobile Number',
- 'Home Address','Home Address 2','Home City','Home State',
- 'Home ZipCode','Home Country','Work Address','Work Address 2',
- 'Work City','Work State','Work ZipCode','Work Country',
- 'Job Title','Department','Organization','Web Page 1',
- 'Web Page 2','Birth Year','Birth Month','Birth Day',
- 'Custom 1','Custom 2','Custom 3','Custom 4','Notes',
- 'Anniversary Year','Anniversary Month','Anniversary Day',
- 'Category','Spouse name');
- my @kmail_fields = ('Formatted Name','Family Name','Given Name',
- 'Additional Names','Honorific Prefixes','Honorific Suffixes',
- 'Nick Name','Birthday','Home Address Street',
- 'Home Address City','Home Address Region',
- 'Home Address Post Code','Home Address Country',
- 'Home Address Label','Business Address Street',
- 'Business Address City','Business Address Region',
- 'Business Address Post Code','Business Address Country',
- 'Business Address Label','Home Phone','Business Phone',
- 'Mobile Phone','Home Fax','Business Fax','Car Phone','ISDN',
- 'Pager','Email Address','Mail Client','Title','Role',
- 'Organisation','Department','Note','Homepage','Profession',
- 'Assistant\'s Name','Manager\'s Name','Partner\'s Name',
- 'Office','IM Address','Anniversary','Blog');
- my @gmail_fields = ('Name','E-mail Address','Notes','E-mail 2 Address',
- 'E-mail 3 Address','Mobile Phone','Pager','Company',
- 'Job Title','Home Phone','Home Phone 2','Home Fax',
- 'Home Address','Business Phone','Business Phone 2',
- 'Business Fax','Business Address','Other Phone','Other Fax',
- 'Other Address','junk');
- my @foxmail_fields = ('First Name','Last Name','Name','Nickname','e-mail Address',
- 'Mobile Phone','Pager Number','QQ','ICQ','Personal Home Page',
- 'Sex','Birthday','Interest','Home Country','Home Province',
- 'Home City','Home Postal Code','Home Street Address',
- 'Home Telephone 1','Home Telephone 2','Home Fax','Office Company',
- 'Office Country','Office Province','Office City',
- 'Office Postal Code','Office Address','Office HomePage',
- 'Office Position','Office Department','Office Telephone 1',
- 'Office Telephone 2','Office Fax','Memo','foxaddrID');
- my @basic_fields = ('Nickname','e-mail Address');
- if (grep m/claws-mail/ => `ps -U $ENV{USER}`) {
- die("You must quit claws-mail before running this script\n");
- }
- if ($csvfile eq "" || $type eq "" || $type !~ m/$known_types/ || $iNeedHelp) {
- if (!$iNeedHelp) {
- if ($csvfile eq "") {
- print "ERROR: Option csv is missing!\n";
- }
- if ($type eq "") {
- print "ERROR: Option type is missing!\n";
- }
- if ($type && $type !~ m/$known_types/) {
- print "ERROR: \"$type\" is an unknown type!\n";
- }
- }
- print qq~
- Usage:
- $script [OPTIONS]
- Options:
- --help Show this screen
- --type=becky|thunderbird|kmail|gmail|foxmail|basic
- Type of exported address book
- --csv=FILENAME Full path to CSV file
- --name="My new address book" Name of new Claws address book (optional)
- ~;
- exit;
- }
- open(INPUT, "<$csvfile") || die("Can't open the CSV file [$csvfile]\n");
- my @csvlines = <INPUT>;
- close INPUT;
- my $config_dir = `claws-mail --config-dir` || die("ERROR:
- You don't appear to have Claws Mail installed\n");
- chomp $config_dir;
- my $claws_version = `claws-mail --version`;
- $claws_version =~ s/^Claws Mail version //;
- my ($major, $minor) = split(/\./, $claws_version);
- my $addr_dir;
- if (($major == 3 && $minor >= 1) || $major > 3) {
- $addr_dir = "$config_dir/addrbook";
- } else {
- $addr_dir = $config_dir;
- }
- my $addr_index = "$addr_dir/addrbook--index.xml";
- my $csv = Text::CSV_XS->new({binary => 1,
- quote_char => $quote_char,
- escape_char => $esc_char,
- sep_char => $sep_char});
- my $csvtitles = shift(@csvlines);
- $csv->parse($csvtitles);
- my @csvfields = $csv->fields;
- check_fields();
- my $new_addrbook = $bookname || get_book_name();
- my $xmlobject = write_xml();
- chdir;
- my @filelist = ();
- opendir(ADDR_DIR, $addr_dir) || die("Can't open $addr_dir directory\n");
- push(@filelist, (readdir(ADDR_DIR)));
- closedir(ADDR_DIR);
- my @files = ();
- foreach my $file (@filelist) {
- if ($file =~ m/^addrbook/ && $file =~ m/[0-9].xml$/) {
- push(@files, "$file");
- }
- }
- my @sorted_files = sort {$a cmp $b} @files;
- my $latest_file = pop(@sorted_files);
- $latest_file =~ s/^addrbook-//;
- $latest_file =~ s/.xml$//;
- $latest_file++;
- my $new_addrbk = "addrbook-"."$latest_file".".xml";
- open (NEWADDR, ">$addr_dir/$new_addrbk");
- print NEWADDR $xmlobject;
- close NEWADDR;
- open (ADDRIN, "<$addr_index") || die("can't open $addr_index for reading");
- my @addrindex_file = <ADDRIN>;
- close ADDRIN;
- my $rw_addrindex;
- foreach my $addrindex_line (@addrindex_file) {
- if ($addrindex_line =~ m/<\/book_list>/) {
- $rw_addrindex .= " <book name=\"$new_addrbook\" "
- ."file=\"$new_addrbk\" />\n </book_list>\n";
- } else {
- $rw_addrindex .= "$addrindex_line";
- }
- }
- open (NEWADDRIN, ">$addr_index") || die("Can't open $addr_index for writing");
- print NEWADDRIN "$rw_addrindex";
- close NEWADDRIN;
- print "Done. Address book imported successfully.\n";
- exit;
- sub get_book_name {
- if ($type eq "becky") {
- return("Becky address book");
- } elsif ($type eq "thunderbird") {
- return("Thunderbird address book");
- } elsif ($type eq "kmail") {
- return("Kmail address book");
- } elsif ($type eq "gmail") {
- return("gmail address book");
- } elsif ($type eq "foxmail") {
- return("foxmail address book");
- } elsif ($type eq "basic") {
- return("basic address book");
- }
- }
- sub check_fields {
- if ($type eq "becky") {
- if ($#csvfields != $#becky_fields) {
- die("ERROR:\n\tInvalid field count!\n"
- ."\tYou need to do a Full Export With Titles\n");
- }
- } elsif ($type eq "thunderbird") {
- if ($#csvfields != $#tbird_fields) {
- die("ERROR:\n\tInvalid field count!\n"
- ."\tProblem with your exported CSV file\n");
- }
- } elsif ($type eq "kmail") {
- if ($#csvfields != $#kmail_fields) {
- die("ERROR:\n\tInvalid field count!\n"
- ."\tProblem with your exported CSV file\n");
- }
- } elsif ($type eq "gmail") {
- if ($#csvfields != $#gmail_fields) {
- die("ERROR:\n\tInvalid field count!\n"
- ."\tProblem with your exported CSV file\n");
- }
- } elsif ($type eq "foxmail") {
- if ($#csvfields != $#foxmail_fields) {
- die("ERROR:\n\tInvalid field count!\n"
- ."\tProblem with your exported CSV file\n");
- }
- } elsif ($type eq "basic") {
- if ($#csvfields != $#basic_fields) {
- die("ERROR:\n\tInvalid field count!\n"
- ."\tProblem with your exported CSV file\n");
- }
- }
- }
- sub write_xml {
- my @std_items = get_items();
- my @input_fields = get_fields();
- my $time = time;
- my $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
- ."<address-book name=\"$new_addrbook\" >\n ";
- my $prev_line;
- foreach my $line (@csvlines) {
- $csv->parse($line);
- my @fields = $csv->fields;
- # check if an entry contains line breaks
- if ($#fields != $#input_fields) {
- if ($prev_line) {
- my $concat_line = $prev_line.$line;
- $csv->parse($concat_line);
- @fields = $csv->fields;
- if ($#fields != $#input_fields) {
- $concat_line =~ s/\r\n$/ /;
- $concat_line =~ s/\n$/ /;
- $prev_line = $concat_line;
- next;
- }
- } else {
- $line =~ s/\r\n$/ /;
- $line =~ s/\n$/ /;
- $prev_line = $line;
- next;
- }
- }
- $prev_line = '';
- @fields = escape_fields(@fields);
- $xml .= "<person uid=\"$time\" "
- ."first-name=\"$fields[$std_items[0]]\" "
- ."last-name=\"$fields[$std_items[1]]\" "
- ."nick-name=\"$fields[$std_items[2]]\" "
- ."cn=\"$fields[$std_items[3]]\">\n ";
- $time++;
- if ($type eq "thunderbird") {
- $xml .= "<address-list>\n "
- ."<address uid=\"$time\" alias=\"\" "
- ."email=\"$fields[$std_items[4]]\" "
- ."remarks=\"\" /> \n";
- $time++;
- if ($fields[$std_items[5]]) {
- $xml .=" <address uid=\"$time\" alias=\"\" "
- ."email=\"$fields[$std_items[5]]\" "
- ."remarks=\"\" /> \n";
- }
- $xml .= " </address-list> \n";
- } elsif ($type eq "foxmail") {
- $xml .= "<address-list>\n ";
- if ($fields[$std_items[4]] =~ m/,/) {
- my @addrs = split(",", $fields[$std_items[4]]);
- my $addr_one = pop(@addrs);
- $xml .= "<address uid=\"$time\" alias=\"\" "
- ."email=\"$addr_one\" "
- ."remarks=\"$fields[$std_items[5]]\" /> \n";
- foreach my $eaddr (@addrs) {
- $time++;
- $xml .= "<address uid=\"$time\" alias=\"\" "
- ."email=\"$eaddr\" "
- ."remarks=\"\" /> \n";
- }
- } else {
- $xml .= "<address uid=\"$time\" alias=\"\" "
- ."email=\"$fields[$std_items[4]]\" "
- ."remarks=\"$fields[$std_items[5]]\" /> \n";
- }
- $xml .= "</address-list> \n";
- } else {
- $xml .= "<address-list>\n "
- ."<address uid=\"$time\" alias=\"\" "
- ."email=\"$fields[$std_items[4]]\" "
- ."remarks=\"$fields[$std_items[5]]\" /> \n"
- ."</address-list> \n";
- }
- $xml .= "<attribute-list>\n";
- foreach my $item (@std_items) {
- delete($fields[$item]);
- }
- foreach my $field (0 .. $#fields) {
- if ($fields[$field]) {
- $time++;
- $xml .= " <attribute uid=\"$time\" "
- ."name=\"$input_fields[$field]\">"
- ."$fields[$field]</attribute>\n";
- }
- }
- $xml .= " </attribute-list>\n "
- ."</person>\n";
- $time++;
- }
- $xml .= "</address-book>\n";
- return $xml;
- }
- sub get_items {
- if ($type eq "becky") {
- return ('10','9','2','0','1','4');
- } elsif ($type eq "thunderbird") {
- return ('0','1','3','2','4','5','38');
- } elsif ($type eq "kmail") {
- return ('2','1','6','0','28','34');
- } elsif ($type eq "gmail") {
- return('0','0','0','0','1','2');
- } elsif ($type eq "foxmail") {
- return ('0','1','3','2','4','33');
- } elsif ($type eq "basic") {
- return ('0','0','0','0','1','0');
- }
- }
- sub get_fields {
- if ($type eq "becky") {
- return(@becky_fields);
- } elsif ($type eq "thunderbird") {
- return(@tbird_fields);
- } elsif ($type eq "kmail") {
- return(@kmail_fields);
- } elsif ($type eq "gmail") {
- return(@gmail_fields);
- } elsif ($type eq "foxmail") {
- return(@foxmail_fields);
- } elsif ($type eq "basic") {
- return (@basic_fields);
- }
- }
- sub escape_fields {
- my (@fields) = @_;
- for (my $item = 0; $item <= $#fields; $item++) {
- $fields[$item] =~ s/^"//;
- $fields[$item] =~ s/"$//;
- $fields[$item] =~ s/"/"/g;
- $fields[$item] =~ s/&/&/g;
- $fields[$item] =~ s/'/'/g;
- $fields[$item] =~ s/</</g;
- $fields[$item] =~ s/>/>/g;
- }
-
- return @fields;
- }
|