4 use Getopt::Long qw(:config pass_through);
7 # * This file is free software; you can redistribute it and/or modify it
8 # * under the terms of the GNU General Public License as published by
9 # * the Free Software Foundation; either version 3 of the License, or
10 # * (at your option) any later version.
12 # * This program is distributed in the hope that it will be useful, but
13 # * WITHOUT ANY WARRANTY; without even the implied warranty of
14 # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # * General Public License for more details.
17 # * You should have received a copy of the GNU General Public License
18 # * along with this program; if not, write to the Free Software
19 # * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 # * Copyright 2007/2008 Paul Mangan <paul@claws-mail.org>
25 # Import CSV exported address books to Claws Mail
26 # Supported address books:
28 # Thunderbird >= 2.0.0.6
29 # Kmail >= 1.9.7 / Kaddressbook >= 3.5.7
30 # ** kmail bug: can export badly formatted csv **
33 # Becky: full export with titles
34 # thunderbird: export as 'comma separated'
35 # kmail/kaddressbook: Export CSV list
43 my $script = "csv2addressbook.pl";
49 my $known_types = qr/^(?:becky|thunderbird|kmail)$/;
51 GetOptions("type=s" => \$type,
53 "name=s" => \$bookname,
54 "help" => \$iNeedHelp);
56 my @becky_fields = ('Name','E-mail Address', 'Nickname (Input shortcut)',
57 'Web Page','Notes','Company','Department','Job Title',
58 'Job Role','Last Name','First Name','Middle Name',
59 'Birthday','Home Phone','Business Phone','Mobile Phone',
60 'Fax','Street','City','State','Postal Code','Country',
62 my @tbird_fields = ('First Name','Last Name','Display Name','Nickname',
63 'Primary Email','Secondary Email','Work Phone',
64 'Home Phone','Fax Number','Pager Number','Mobile Number',
65 'Home Address','Home Address 2','Home City','Home State',
66 'Home ZipCode','Home Country','Work Address','Work Address 2',
67 'Work City','Work State','Work ZipCode','Work Country',
68 'Job Title','Department','Organization','Web Page 1',
69 'Web Page 2','Birth Year','Birth Month','Birth Day',
70 'Custom 1','Custom 2','Custom 3','Custom 4','Notes','junk');
71 my @kmail_fields = ('Formatted Name','Family Name','Given Name',
72 'Additional Names','Honorific Prefixes','Honorific Suffixes',
73 'Nick Name','Birthday','Home Address Street',
74 'Home Address City','Home Address Region',
75 'Home Address Post Code','Home Address Country',
76 'Home Address Label','Business Address Street',
77 'Business Address City','Business Address Region',
78 'Business Address Post Code','Business Address Country',
79 'Business Address Label','Home Phone','Business Phone',
80 'Mobile Phone','Home Fax','Business Fax','Car Phone','ISDN',
81 'Pager','Email Address','Mail Client','Title','Role',
82 'Organisation','Department','Note','Homepage','Profession',
83 'Assistant\'s Name','Manager\'s Name','Partner\'s Name',
84 'Office','IM Address','Anniversary','Blog');
86 if (grep m/claws-mail/ => `ps -U $ENV{USER}`) {
87 die("You must quit claws-mail before running this script\n");
90 if ($csvfile eq "" || $type eq "" || $type !~ m/$known_types/ || $iNeedHelp) {
93 print "ERROR: Option csv is missing!\n";
96 print "ERROR: Option type is missing!\n";
98 if ($type && $type !~ m/$known_types/) {
99 print "ERROR: \"$type\" is an unknown type!\n";
106 --help Show this screen
107 --type=becky|thunderbird|kmail Type of exported address book
108 --csv=FILENAME Full path to CSV file
109 --name="My new address book" Name of new Claws address book (optional)
114 open(INPUT, "<$csvfile") || die("Can't open the CSV file [$csvfile]\n");
115 my @csvlines = <INPUT>;
118 my $config_dir = `claws-mail --config-dir` || die("ERROR:
119 You don't appear to have Claws Mail installed\n");
122 my $claws_version = `claws-mail --version`;
123 $claws_version =~ s/^Claws Mail version //;
125 my ($major, $minor) = split(/\./, $claws_version);
129 if (($major == 3 && $minor >= 1) || $major > 3) {
130 $addr_dir = "$config_dir/addrbook";
132 $addr_dir = $config_dir;
135 my $addr_index = "$addr_dir/addrbook--index.xml";
136 my $csv = Text::CSV_XS->new({binary => 1,
137 quote_char => $quote_char,
138 escape_char => $esc_char,
139 sep_char => $sep_char});
141 my $csvtitles = shift(@csvlines);
143 $csv->parse($csvtitles);
144 my @csvfields = $csv->fields;
148 my $new_addrbook = $bookname || get_book_name();
150 my $xmlobject = write_xml();
155 opendir(ADDR_DIR, $addr_dir) || die("Can't open $addr_dir directory\n");
156 push(@filelist, (readdir(ADDR_DIR)));
160 foreach my $file (@filelist) {
161 if ($file =~ m/^addrbook/ && $file =~ m/[0-9].xml$/) {
162 push(@files, "$file");
166 my @sorted_files = sort {$a cmp $b} @files;
167 my $latest_file = pop(@sorted_files);
168 $latest_file =~ s/^addrbook-//;
169 $latest_file =~ s/.xml$//;
171 my $new_addrbk = "addrbook-"."$latest_file".".xml";
173 open (NEWADDR, ">$addr_dir/$new_addrbk");
174 print NEWADDR $xmlobject;
177 open (ADDRIN, "<$addr_index") || die("can't open $addr_index for reading");
178 my @addrindex_file = <ADDRIN>;
182 foreach my $addrindex_line (@addrindex_file) {
183 if ($addrindex_line =~ m/<\/book_list>/) {
184 $rw_addrindex .= " <book name=\"$new_addrbook\" "
185 ."file=\"$new_addrbk\" />\n </book_list>\n";
187 $rw_addrindex .= "$addrindex_line";
191 open (NEWADDRIN, ">$addr_index") || die("Can't open $addr_index for writing");
192 print NEWADDRIN "$rw_addrindex";
195 print "Done. Address book imported successfully.\n";
200 if ($type eq "becky") {
201 return("Becky address book");
202 } elsif ($type eq "thunderbird") {
203 return("Thunderbird address book");
204 } elsif ($type eq "kmail") {
205 return("Kmail address book");
210 if ($type eq "becky") {
211 if ($#csvfields != $#becky_fields) {
212 die("ERROR:\n\tNot enough fields!\n"
213 ."\tYou need to do a Full Export With Titles\n");
215 } elsif ($type eq "thunderbird") {
216 if ($#csvfields != $#tbird_fields) {
217 die("ERROR:\n\tNot enough fields!\n"
218 ."\tProblem with your exported CSV file\n");
220 } elsif ($type eq "kmail") {
221 if ($#csvfields != $#kmail_fields) {
222 die("ERROR:\n\tNot enough fields!\n"
223 ."\tProblem with your exported CSV file\n");
229 my @std_items = get_items();
230 my @input_fields = get_fields();
233 my $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
234 ."<address-book name=\"$new_addrbook\" >\n ";
238 foreach my $line (@csvlines) {
240 my @fields = $csv->fields;
241 # check if an entry contains line breaks
242 if ($#fields != $#input_fields) {
244 my $concat_line = $prev_line.$line;
245 $csv->parse($concat_line);
246 @fields = $csv->fields;
247 if ($#fields != $#input_fields) {
248 $concat_line =~ s/\r\n$/ /;
249 $concat_line =~ s/\n$/ /;
250 $prev_line = $concat_line;
262 @fields = escape_fields(@fields);
264 $xml .= "<person uid=\"$time\" "
265 ."first-name=\"$fields[$std_items[0]]\" "
266 ."last-name=\"$fields[$std_items[1]]\" "
267 ."nick-name=\"$fields[$std_items[2]]\" "
268 ."cn=\"$fields[$std_items[3]]\">\n ";
270 if ($type eq "thunderbird") {
271 $xml .= "<address-list>\n "
272 ."<address uid=\"$time\" alias=\"\" "
273 ."email=\"$fields[$std_items[4]]\" "
274 ."remarks=\"\" /> \n";
276 if ($fields[$std_items[5]]) {
277 $xml .=" <address uid=\"$time\" alias=\"\" "
278 ."email=\"$fields[$std_items[5]]\" "
279 ."remarks=\"\" /> \n";
281 $xml .= " </address-list> \n";
283 $xml .= "<address-list>\n "
284 ."<address uid=\"$time\" alias=\"\" "
285 ."email=\"$fields[$std_items[4]]\" "
286 ."remarks=\"$fields[$std_items[5]]\" /> \n"
287 ."</address-list> \n";
289 $xml .= "<attribute-list>\n";
290 foreach my $item (@std_items) {
291 delete($fields[$item]);
293 foreach my $field (0 .. $#fields) {
294 if ($fields[$field]) {
296 $xml .= " <attribute uid=\"$time\" "
297 ."name=\"$input_fields[$field]\">"
298 ."$fields[$field]</attribute>\n";
301 $xml .= " </attribute-list>\n "
306 $xml .= "</address-book>\n";
312 if ($type eq "becky") {
313 return ('10','9','2','0','1','4');
314 } elsif ($type eq "thunderbird") {
315 return ('0','1','3','2','4','5','38');
316 } elsif ($type eq "kmail") {
317 return ('2','1','6','0','28','34');
322 if ($type eq "becky") {
323 return(@becky_fields);
324 } elsif ($type eq "thunderbird") {
325 return(@tbird_fields);
326 } elsif ($type eq "kmail") {
327 return(@kmail_fields);
334 for (my $item = 0; $item <= $#fields; $item++) {
335 $fields[$item] =~ s/^"//;
336 $fields[$item] =~ s/"$//;
337 $fields[$item] =~ s/"/"/g;
338 $fields[$item] =~ s/&/&/g;
339 $fields[$item] =~ s/'/'/g;
340 $fields[$item] =~ s/</</g;
341 $fields[$item] =~ s/>/>/g;