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 **
35 # Becky: full export with titles
36 # thunderbird: export as 'comma separated'
37 # kmail/kaddressbook: Export CSV list
38 # gmail: export Outlook format
39 # foxmail: export with all possible headers
47 my $script = "csv2addressbook.pl";
53 my $known_types = qr/^(?:becky|thunderbird|kmail|gmail|foxmail)$/;
55 GetOptions("type=s" => \$type,
57 "name=s" => \$bookname,
58 "help" => \$iNeedHelp);
60 my @becky_fields = ('Name','E-mail Address', 'Nickname (Input shortcut)',
61 'Web Page','Notes','Company','Department','Job Title',
62 'Job Role','Last Name','First Name','Middle Name',
63 'Birthday','Home Phone','Business Phone','Mobile Phone',
64 'Fax','Street','City','State','Postal Code','Country',
66 my @tbird_fields = ('First Name','Last Name','Display Name','Nickname',
67 'Primary Email','Secondary Email','Work Phone',
68 'Home Phone','Fax Number','Pager Number','Mobile Number',
69 'Home Address','Home Address 2','Home City','Home State',
70 'Home ZipCode','Home Country','Work Address','Work Address 2',
71 'Work City','Work State','Work ZipCode','Work Country',
72 'Job Title','Department','Organization','Web Page 1',
73 'Web Page 2','Birth Year','Birth Month','Birth Day',
74 'Custom 1','Custom 2','Custom 3','Custom 4','Notes','junk');
75 my @kmail_fields = ('Formatted Name','Family Name','Given Name',
76 'Additional Names','Honorific Prefixes','Honorific Suffixes',
77 'Nick Name','Birthday','Home Address Street',
78 'Home Address City','Home Address Region',
79 'Home Address Post Code','Home Address Country',
80 'Home Address Label','Business Address Street',
81 'Business Address City','Business Address Region',
82 'Business Address Post Code','Business Address Country',
83 'Business Address Label','Home Phone','Business Phone',
84 'Mobile Phone','Home Fax','Business Fax','Car Phone','ISDN',
85 'Pager','Email Address','Mail Client','Title','Role',
86 'Organisation','Department','Note','Homepage','Profession',
87 'Assistant\'s Name','Manager\'s Name','Partner\'s Name',
88 'Office','IM Address','Anniversary','Blog');
89 my @gmail_fields = ('Name','E-mail Address','Notes','E-mail 2 Address',
90 'E-mail 3 Address','Mobile Phone','Pager','Company',
91 'Job Title','Home Phone','Home Phone 2','Home Fax',
92 'Home Address','Business Phone','Business Phone 2',
93 'Business Fax','Business Address','Other Phone','Other Fax',
94 'Other Address','junk');
95 my @foxmail_fields = ('First Name','Last Name','Name','Nickname','e-mail Address',
96 'Mobile Phone','Pager Number','QQ','ICQ','Personal Home Page',
97 'Sex','Birthday','Interest','Home Country','Home Province',
98 'Home City','Home Postal Code','Home Street Address',
99 'Home Telephone 1','Home Telephone 2','Home Fax','Office Company',
100 'Office Country','Office Province','Office City',
101 'Office Postal Code','Office Address','Office HomePage',
102 'Office Position','Office Department','Office Telephone 1',
103 'Office Telephone 2','Office Fax','Memo','foxaddrID');
105 if (grep m/claws-mail/ => `ps -U $ENV{USER}`) {
106 die("You must quit claws-mail before running this script\n");
109 if ($csvfile eq "" || $type eq "" || $type !~ m/$known_types/ || $iNeedHelp) {
111 if ($csvfile eq "") {
112 print "ERROR: Option csv is missing!\n";
115 print "ERROR: Option type is missing!\n";
117 if ($type && $type !~ m/$known_types/) {
118 print "ERROR: \"$type\" is an unknown type!\n";
125 --help Show this screen
126 --type=becky|thunderbird|kmail|gmail|foxmail
127 Type of exported address book
128 --csv=FILENAME Full path to CSV file
129 --name="My new address book" Name of new Claws address book (optional)
134 open(INPUT, "<$csvfile") || die("Can't open the CSV file [$csvfile]\n");
135 my @csvlines = <INPUT>;
138 my $config_dir = `claws-mail --config-dir` || die("ERROR:
139 You don't appear to have Claws Mail installed\n");
142 my $claws_version = `claws-mail --version`;
143 $claws_version =~ s/^Claws Mail version //;
145 my ($major, $minor) = split(/\./, $claws_version);
149 if (($major == 3 && $minor >= 1) || $major > 3) {
150 $addr_dir = "$config_dir/addrbook";
152 $addr_dir = $config_dir;
155 my $addr_index = "$addr_dir/addrbook--index.xml";
156 my $csv = Text::CSV_XS->new({binary => 1,
157 quote_char => $quote_char,
158 escape_char => $esc_char,
159 sep_char => $sep_char});
161 my $csvtitles = shift(@csvlines);
163 $csv->parse($csvtitles);
164 my @csvfields = $csv->fields;
168 my $new_addrbook = $bookname || get_book_name();
170 my $xmlobject = write_xml();
175 opendir(ADDR_DIR, $addr_dir) || die("Can't open $addr_dir directory\n");
176 push(@filelist, (readdir(ADDR_DIR)));
180 foreach my $file (@filelist) {
181 if ($file =~ m/^addrbook/ && $file =~ m/[0-9].xml$/) {
182 push(@files, "$file");
186 my @sorted_files = sort {$a cmp $b} @files;
187 my $latest_file = pop(@sorted_files);
188 $latest_file =~ s/^addrbook-//;
189 $latest_file =~ s/.xml$//;
191 my $new_addrbk = "addrbook-"."$latest_file".".xml";
193 open (NEWADDR, ">$addr_dir/$new_addrbk");
194 print NEWADDR $xmlobject;
197 open (ADDRIN, "<$addr_index") || die("can't open $addr_index for reading");
198 my @addrindex_file = <ADDRIN>;
202 foreach my $addrindex_line (@addrindex_file) {
203 if ($addrindex_line =~ m/<\/book_list>/) {
204 $rw_addrindex .= " <book name=\"$new_addrbook\" "
205 ."file=\"$new_addrbk\" />\n </book_list>\n";
207 $rw_addrindex .= "$addrindex_line";
211 open (NEWADDRIN, ">$addr_index") || die("Can't open $addr_index for writing");
212 print NEWADDRIN "$rw_addrindex";
215 print "Done. Address book imported successfully.\n";
220 if ($type eq "becky") {
221 return("Becky address book");
222 } elsif ($type eq "thunderbird") {
223 return("Thunderbird address book");
224 } elsif ($type eq "kmail") {
225 return("Kmail address book");
226 } elsif ($type eq "gmail") {
227 return("gmail address book");
228 } elsif ($type eq "foxmail") {
229 return("foxmail address book");
234 if ($type eq "becky") {
235 if ($#csvfields != $#becky_fields) {
236 die("ERROR:\n\tInvalid field count!\n"
237 ."\tYou need to do a Full Export With Titles\n");
239 } elsif ($type eq "thunderbird") {
240 if ($#csvfields != $#tbird_fields) {
241 die("ERROR:\n\tInvalid field count!\n"
242 ."\tProblem with your exported CSV file\n");
244 } elsif ($type eq "kmail") {
245 if ($#csvfields != $#kmail_fields) {
246 die("ERROR:\n\tInvalid field count!\n"
247 ."\tProblem with your exported CSV file\n");
249 } elsif ($type eq "gmail") {
250 if ($#csvfields != $#gmail_fields) {
251 die("ERROR:\n\tInvalid field count!\n"
252 ."\tProblem with your exported CSV file\n");
254 } elsif ($type eq "foxmail") {
255 if ($#csvfields != $#foxmail_fields) {
256 die("ERROR:\n\tInvalid field count!\n"
257 ."\tProblem with your exported CSV file\n");
263 my @std_items = get_items();
264 my @input_fields = get_fields();
267 my $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
268 ."<address-book name=\"$new_addrbook\" >\n ";
272 foreach my $line (@csvlines) {
274 my @fields = $csv->fields;
275 # check if an entry contains line breaks
276 if ($#fields != $#input_fields) {
278 my $concat_line = $prev_line.$line;
279 $csv->parse($concat_line);
280 @fields = $csv->fields;
281 if ($#fields != $#input_fields) {
282 $concat_line =~ s/\r\n$/ /;
283 $concat_line =~ s/\n$/ /;
284 $prev_line = $concat_line;
296 @fields = escape_fields(@fields);
298 $xml .= "<person uid=\"$time\" "
299 ."first-name=\"$fields[$std_items[0]]\" "
300 ."last-name=\"$fields[$std_items[1]]\" "
301 ."nick-name=\"$fields[$std_items[2]]\" "
302 ."cn=\"$fields[$std_items[3]]\">\n ";
304 if ($type eq "thunderbird") {
305 $xml .= "<address-list>\n "
306 ."<address uid=\"$time\" alias=\"\" "
307 ."email=\"$fields[$std_items[4]]\" "
308 ."remarks=\"\" /> \n";
310 if ($fields[$std_items[5]]) {
311 $xml .=" <address uid=\"$time\" alias=\"\" "
312 ."email=\"$fields[$std_items[5]]\" "
313 ."remarks=\"\" /> \n";
315 $xml .= " </address-list> \n";
316 } elsif ($type eq "foxmail") {
317 $xml .= "<address-list>\n ";
318 if ($fields[$std_items[4]] =~ m/,/) {
319 my @addrs = split(",", $fields[$std_items[4]]);
320 my $addr_one = pop(@addrs);
321 $xml .= "<address uid=\"$time\" alias=\"\" "
322 ."email=\"$addr_one\" "
323 ."remarks=\"$fields[$std_items[5]]\" /> \n";
324 foreach my $eaddr (@addrs) {
326 $xml .= "<address uid=\"$time\" alias=\"\" "
328 ."remarks=\"\" /> \n";
331 $xml .= "<address uid=\"$time\" alias=\"\" "
332 ."email=\"$fields[$std_items[4]]\" "
333 ."remarks=\"$fields[$std_items[5]]\" /> \n";
335 $xml .= "</address-list> \n";
337 $xml .= "<address-list>\n "
338 ."<address uid=\"$time\" alias=\"\" "
339 ."email=\"$fields[$std_items[4]]\" "
340 ."remarks=\"$fields[$std_items[5]]\" /> \n"
341 ."</address-list> \n";
343 $xml .= "<attribute-list>\n";
344 foreach my $item (@std_items) {
345 delete($fields[$item]);
347 foreach my $field (0 .. $#fields) {
348 if ($fields[$field]) {
350 $xml .= " <attribute uid=\"$time\" "
351 ."name=\"$input_fields[$field]\">"
352 ."$fields[$field]</attribute>\n";
355 $xml .= " </attribute-list>\n "
360 $xml .= "</address-book>\n";
366 if ($type eq "becky") {
367 return ('10','9','2','0','1','4');
368 } elsif ($type eq "thunderbird") {
369 return ('0','1','3','2','4','5','38');
370 } elsif ($type eq "kmail") {
371 return ('2','1','6','0','28','34');
372 } elsif ($type eq "gmail") {
373 return('0','0','0','0','1','2');
374 } elsif ($type eq "foxmail") {
375 return ('0','1','3','2','4','33');
380 if ($type eq "becky") {
381 return(@becky_fields);
382 } elsif ($type eq "thunderbird") {
383 return(@tbird_fields);
384 } elsif ($type eq "kmail") {
385 return(@kmail_fields);
386 } elsif ($type eq "gmail") {
387 return(@gmail_fields);
388 } elsif ($type eq "foxmail") {
389 return(@foxmail_fields);
396 for (my $item = 0; $item <= $#fields; $item++) {
397 $fields[$item] =~ s/^"//;
398 $fields[$item] =~ s/"$//;
399 $fields[$item] =~ s/"/"/g;
400 $fields[$item] =~ s/&/&/g;
401 $fields[$item] =~ s/'/'/g;
402 $fields[$item] =~ s/</</g;
403 $fields[$item] =~ s/>/>/g;