Fix some odd debug outputs
[claws.git] / tools / csv2addressbook.pl
blob309cfd54c761c402163e3b7d39e6b984b2ec81d7
1 #!/usr/bin/perl -w
3 use strict;
4 use Getopt::Long qw(:config pass_through);
5 use Text::CSV_XS;
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.
11 # *
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.
16 # *
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.
20 # *
21 # * Copyright 2007/2008 Paul Mangan <paul@claws-mail.org>
22 # *
25 # Import CSV exported address books to Claws Mail
26 # Supported address books:
27 # Becky >= 2.41
28 # Thunderbird >= 2.0.0.6
29 # Kmail >= 1.9.7 / Kaddressbook >= 3.5.7
30 # ** kmail bug: can export badly formatted csv **
31 # Gmail
32 # Fox Mail
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
41 ###
42 my $quote_char = '"';
43 my $esc_char = '"';
44 my $sep_char = ',';
45 ###
47 my $script = "csv2addressbook.pl";
48 my $type = '';
49 my $csvfile = '';
50 my $bookname = '';
51 my $iNeedHelp = '';
53 my $known_types = qr/^(?:becky|thunderbird|kmail|gmail|foxmail)$/;
55 GetOptions("type=s" => \$type,
56 "csv=s" => \$csvfile,
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',
65 'Delivery Label');
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',
75 'Anniversary Year','Anniversary Month','Anniversary Day',
76 'Category','Spouse name');
77 my @kmail_fields = ('Formatted Name','Family Name','Given Name',
78 'Additional Names','Honorific Prefixes','Honorific Suffixes',
79 'Nick Name','Birthday','Home Address Street',
80 'Home Address City','Home Address Region',
81 'Home Address Post Code','Home Address Country',
82 'Home Address Label','Business Address Street',
83 'Business Address City','Business Address Region',
84 'Business Address Post Code','Business Address Country',
85 'Business Address Label','Home Phone','Business Phone',
86 'Mobile Phone','Home Fax','Business Fax','Car Phone','ISDN',
87 'Pager','Email Address','Mail Client','Title','Role',
88 'Organisation','Department','Note','Homepage','Profession',
89 'Assistant\'s Name','Manager\'s Name','Partner\'s Name',
90 'Office','IM Address','Anniversary','Blog');
91 my @gmail_fields = ('Name','E-mail Address','Notes','E-mail 2 Address',
92 'E-mail 3 Address','Mobile Phone','Pager','Company',
93 'Job Title','Home Phone','Home Phone 2','Home Fax',
94 'Home Address','Business Phone','Business Phone 2',
95 'Business Fax','Business Address','Other Phone','Other Fax',
96 'Other Address','junk');
97 my @foxmail_fields = ('First Name','Last Name','Name','Nickname','e-mail Address',
98 'Mobile Phone','Pager Number','QQ','ICQ','Personal Home Page',
99 'Sex','Birthday','Interest','Home Country','Home Province',
100 'Home City','Home Postal Code','Home Street Address',
101 'Home Telephone 1','Home Telephone 2','Home Fax','Office Company',
102 'Office Country','Office Province','Office City',
103 'Office Postal Code','Office Address','Office HomePage',
104 'Office Position','Office Department','Office Telephone 1',
105 'Office Telephone 2','Office Fax','Memo','foxaddrID');
107 if (grep m/claws-mail/ => `ps -U $ENV{USER}`) {
108 die("You must quit claws-mail before running this script\n");
111 if ($csvfile eq "" || $type eq "" || $type !~ m/$known_types/ || $iNeedHelp) {
112 if (!$iNeedHelp) {
113 if ($csvfile eq "") {
114 print "ERROR: Option csv is missing!\n";
116 if ($type eq "") {
117 print "ERROR: Option type is missing!\n";
119 if ($type && $type !~ m/$known_types/) {
120 print "ERROR: \"$type\" is an unknown type!\n";
123 print qq~
124 Usage:
125 $script [OPTIONS]
126 Options:
127 --help Show this screen
128 --type=becky|thunderbird|kmail|gmail|foxmail
129 Type of exported address book
130 --csv=FILENAME Full path to CSV file
131 --name="My new address book" Name of new Claws address book (optional)
133 exit;
136 open(INPUT, "<$csvfile") || die("Can't open the CSV file [$csvfile]\n");
137 my @csvlines = <INPUT>;
138 close INPUT;
140 my $config_dir = `claws-mail --config-dir` || die("ERROR:
141 You don't appear to have Claws Mail installed\n");
142 chomp $config_dir;
144 my $claws_version = `claws-mail --version`;
145 $claws_version =~ s/^Claws Mail version //;
147 my ($major, $minor) = split(/\./, $claws_version);
149 my $addr_dir;
151 if (($major == 3 && $minor >= 1) || $major > 3) {
152 $addr_dir = "$config_dir/addrbook";
153 } else {
154 $addr_dir = $config_dir;
157 my $addr_index = "$addr_dir/addrbook--index.xml";
158 my $csv = Text::CSV_XS->new({binary => 1,
159 quote_char => $quote_char,
160 escape_char => $esc_char,
161 sep_char => $sep_char});
163 my $csvtitles = shift(@csvlines);
165 $csv->parse($csvtitles);
166 my @csvfields = $csv->fields;
168 check_fields();
170 my $new_addrbook = $bookname || get_book_name();
172 my $xmlobject = write_xml();
174 chdir;
176 my @filelist = ();
177 opendir(ADDR_DIR, $addr_dir) || die("Can't open $addr_dir directory\n");
178 push(@filelist, (readdir(ADDR_DIR)));
179 closedir(ADDR_DIR);
181 my @files = ();
182 foreach my $file (@filelist) {
183 if ($file =~ m/^addrbook/ && $file =~ m/[0-9].xml$/) {
184 push(@files, "$file");
188 my @sorted_files = sort {$a cmp $b} @files;
189 my $latest_file = pop(@sorted_files);
190 $latest_file =~ s/^addrbook-//;
191 $latest_file =~ s/.xml$//;
192 $latest_file++;
193 my $new_addrbk = "addrbook-"."$latest_file".".xml";
195 open (NEWADDR, ">$addr_dir/$new_addrbk");
196 print NEWADDR $xmlobject;
197 close NEWADDR;
199 open (ADDRIN, "<$addr_index") || die("can't open $addr_index for reading");
200 my @addrindex_file = <ADDRIN>;
201 close ADDRIN;
203 my $rw_addrindex;
204 foreach my $addrindex_line (@addrindex_file) {
205 if ($addrindex_line =~ m/<\/book_list>/) {
206 $rw_addrindex .= " <book name=\"$new_addrbook\" "
207 ."file=\"$new_addrbk\" />\n </book_list>\n";
208 } else {
209 $rw_addrindex .= "$addrindex_line";
213 open (NEWADDRIN, ">$addr_index") || die("Can't open $addr_index for writing");
214 print NEWADDRIN "$rw_addrindex";
215 close NEWADDRIN;
217 print "Done. Address book imported successfully.\n";
219 exit;
221 sub get_book_name {
222 if ($type eq "becky") {
223 return("Becky address book");
224 } elsif ($type eq "thunderbird") {
225 return("Thunderbird address book");
226 } elsif ($type eq "kmail") {
227 return("Kmail address book");
228 } elsif ($type eq "gmail") {
229 return("gmail address book");
230 } elsif ($type eq "foxmail") {
231 return("foxmail address book");
235 sub check_fields {
236 if ($type eq "becky") {
237 if ($#csvfields != $#becky_fields) {
238 die("ERROR:\n\tInvalid field count!\n"
239 ."\tYou need to do a Full Export With Titles\n");
241 } elsif ($type eq "thunderbird") {
242 if ($#csvfields != $#tbird_fields) {
243 die("ERROR:\n\tInvalid field count!\n"
244 ."\tProblem with your exported CSV file\n");
246 } elsif ($type eq "kmail") {
247 if ($#csvfields != $#kmail_fields) {
248 die("ERROR:\n\tInvalid field count!\n"
249 ."\tProblem with your exported CSV file\n");
251 } elsif ($type eq "gmail") {
252 if ($#csvfields != $#gmail_fields) {
253 die("ERROR:\n\tInvalid field count!\n"
254 ."\tProblem with your exported CSV file\n");
256 } elsif ($type eq "foxmail") {
257 if ($#csvfields != $#foxmail_fields) {
258 die("ERROR:\n\tInvalid field count!\n"
259 ."\tProblem with your exported CSV file\n");
264 sub write_xml {
265 my @std_items = get_items();
266 my @input_fields = get_fields();
268 my $time = time;
269 my $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
270 ."<address-book name=\"$new_addrbook\" >\n ";
272 my $prev_line;
274 foreach my $line (@csvlines) {
275 $csv->parse($line);
276 my @fields = $csv->fields;
277 # check if an entry contains line breaks
278 if ($#fields != $#input_fields) {
279 if ($prev_line) {
280 my $concat_line = $prev_line.$line;
281 $csv->parse($concat_line);
282 @fields = $csv->fields;
283 if ($#fields != $#input_fields) {
284 $concat_line =~ s/\r\n$/ /;
285 $concat_line =~ s/\n$/ /;
286 $prev_line = $concat_line;
287 next;
289 } else {
290 $line =~ s/\r\n$/ /;
291 $line =~ s/\n$/ /;
292 $prev_line = $line;
293 next;
296 $prev_line = '';
298 @fields = escape_fields(@fields);
300 $xml .= "<person uid=\"$time\" "
301 ."first-name=\"$fields[$std_items[0]]\" "
302 ."last-name=\"$fields[$std_items[1]]\" "
303 ."nick-name=\"$fields[$std_items[2]]\" "
304 ."cn=\"$fields[$std_items[3]]\">\n ";
305 $time++;
306 if ($type eq "thunderbird") {
307 $xml .= "<address-list>\n "
308 ."<address uid=\"$time\" alias=\"\" "
309 ."email=\"$fields[$std_items[4]]\" "
310 ."remarks=\"\" /> \n";
311 $time++;
312 if ($fields[$std_items[5]]) {
313 $xml .=" <address uid=\"$time\" alias=\"\" "
314 ."email=\"$fields[$std_items[5]]\" "
315 ."remarks=\"\" /> \n";
317 $xml .= " </address-list> \n";
318 } elsif ($type eq "foxmail") {
319 $xml .= "<address-list>\n ";
320 if ($fields[$std_items[4]] =~ m/,/) {
321 my @addrs = split(",", $fields[$std_items[4]]);
322 my $addr_one = pop(@addrs);
323 $xml .= "<address uid=\"$time\" alias=\"\" "
324 ."email=\"$addr_one\" "
325 ."remarks=\"$fields[$std_items[5]]\" /> \n";
326 foreach my $eaddr (@addrs) {
327 $time++;
328 $xml .= "<address uid=\"$time\" alias=\"\" "
329 ."email=\"$eaddr\" "
330 ."remarks=\"\" /> \n";
332 } else {
333 $xml .= "<address uid=\"$time\" alias=\"\" "
334 ."email=\"$fields[$std_items[4]]\" "
335 ."remarks=\"$fields[$std_items[5]]\" /> \n";
337 $xml .= "</address-list> \n";
338 } else {
339 $xml .= "<address-list>\n "
340 ."<address uid=\"$time\" alias=\"\" "
341 ."email=\"$fields[$std_items[4]]\" "
342 ."remarks=\"$fields[$std_items[5]]\" /> \n"
343 ."</address-list> \n";
345 $xml .= "<attribute-list>\n";
346 foreach my $item (@std_items) {
347 delete($fields[$item]);
349 foreach my $field (0 .. $#fields) {
350 if ($fields[$field]) {
351 $time++;
352 $xml .= " <attribute uid=\"$time\" "
353 ."name=\"$input_fields[$field]\">"
354 ."$fields[$field]</attribute>\n";
357 $xml .= " </attribute-list>\n "
358 ."</person>\n";
359 $time++;
362 $xml .= "</address-book>\n";
364 return $xml;
367 sub get_items {
368 if ($type eq "becky") {
369 return ('10','9','2','0','1','4');
370 } elsif ($type eq "thunderbird") {
371 return ('0','1','3','2','4','5','38');
372 } elsif ($type eq "kmail") {
373 return ('2','1','6','0','28','34');
374 } elsif ($type eq "gmail") {
375 return('0','0','0','0','1','2');
376 } elsif ($type eq "foxmail") {
377 return ('0','1','3','2','4','33');
381 sub get_fields {
382 if ($type eq "becky") {
383 return(@becky_fields);
384 } elsif ($type eq "thunderbird") {
385 return(@tbird_fields);
386 } elsif ($type eq "kmail") {
387 return(@kmail_fields);
388 } elsif ($type eq "gmail") {
389 return(@gmail_fields);
390 } elsif ($type eq "foxmail") {
391 return(@foxmail_fields);
395 sub escape_fields {
396 my (@fields) = @_;
398 for (my $item = 0; $item <= $#fields; $item++) {
399 $fields[$item] =~ s/^"//;
400 $fields[$item] =~ s/"$//;
401 $fields[$item] =~ s/"/&quot;/g;
402 $fields[$item] =~ s/&/&amp;/g;
403 $fields[$item] =~ s/'/&apos;/g;
404 $fields[$item] =~ s/</&lt;/g;
405 $fields[$item] =~ s/>/&gt;/g;
408 return @fields;