2008-03-12 [paul] 3.3.1cvs21
[claws.git] / tools / csv2addressbook.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Getopt::Long qw(:config pass_through);
5 use Text::CSV_XS;
6
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 #  *
23
24 #
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 #
32
33 # Becky: full export with titles
34 # thunderbird: export as 'comma separated'
35 # kmail/kaddressbook: Export CSV list
36
37 ###
38 my $quote_char = '"';
39 my $esc_char = '"';
40 my $sep_char = ',';
41 ###
42
43 my $script = "csv2addressbook.pl";
44 my $type = '';
45 my $csvfile = '';
46 my $bookname = '';
47 my $iNeedHelp = '';
48
49 my $known_types = qr/^(?:becky|thunderbird|kmail)$/;
50
51 GetOptions("type=s" => \$type,
52            "csv=s"  => \$csvfile,
53            "name=s" => \$bookname,
54            "help"   => \$iNeedHelp);
55
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',
61                     'Delivery Label');
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');
85
86 if (grep m/claws-mail/ => `ps -U $ENV{USER}`) {
87         die("You must quit claws-mail before running this script\n");
88 }
89
90 if ($csvfile eq "" || $type eq "" || $type !~ m/$known_types/ || $iNeedHelp) {
91         if (!$iNeedHelp) {
92                 if ($csvfile eq "") {
93                         print "ERROR: Option csv is missing!\n";
94                 }
95                 if ($type eq "") {
96                         print "ERROR: Option type is missing!\n";
97                 }
98                 if ($type && $type !~ m/$known_types/) {
99                         print "ERROR: \"$type\" is an unknown type!\n";
100                 }
101         }
102         print qq~
103 Usage:
104         $script [OPTIONS]
105 Options:
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)
110 ~;
111 exit;
112 }
113
114 open(INPUT, "<$csvfile") || die("Can't open the CSV file [$csvfile]\n");
115         my @csvlines = <INPUT>;
116 close INPUT;
117
118 my $config_dir = `claws-mail --config-dir` || die("ERROR:
119         You don't appear to have Claws Mail installed\n");
120 chomp $config_dir;
121
122 my $claws_version = `claws-mail --version`;
123 $claws_version =~ s/^Claws Mail version //;
124
125 my ($major, $minor) = split(/\./, $claws_version);
126
127 my $addr_dir;
128
129 if (($major == 3 && $minor >= 1) || $major > 3) {
130         $addr_dir = "$config_dir/addrbook";
131 } else {
132         $addr_dir = $config_dir;
133 }
134
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});
140
141 my $csvtitles = shift(@csvlines);
142
143 $csv->parse($csvtitles);
144 my @csvfields = $csv->fields;
145
146 check_fields();
147
148 my $new_addrbook = $bookname || get_book_name();
149
150 my $xmlobject = write_xml();
151
152 chdir;
153
154 my @filelist = ();
155 opendir(ADDR_DIR, $addr_dir) || die("Can't open $addr_dir directory\n");
156         push(@filelist, (readdir(ADDR_DIR)));
157 closedir(ADDR_DIR);
158
159 my @files = ();
160 foreach my $file (@filelist) {
161         if ($file =~ m/^addrbook/ && $file =~ m/[0-9].xml$/) {
162                 push(@files, "$file");
163         }
164 }
165
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$//;
170 $latest_file++;
171 my $new_addrbk = "addrbook-"."$latest_file".".xml";
172
173 open (NEWADDR, ">$addr_dir/$new_addrbk");
174 print NEWADDR $xmlobject;
175 close NEWADDR;
176
177 open (ADDRIN, "<$addr_index") || die("can't open $addr_index for reading");
178         my @addrindex_file = <ADDRIN>;
179 close ADDRIN;
180
181 my $rw_addrindex;
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";
186         } else {
187                 $rw_addrindex .= "$addrindex_line";
188         }
189 }
190
191 open (NEWADDRIN, ">$addr_index") || die("Can't open $addr_index for writing");
192 print NEWADDRIN "$rw_addrindex";
193 close NEWADDRIN;
194
195 print "Done. Address book imported successfully.\n";
196
197 exit;
198
199 sub get_book_name {
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");
206         }
207 }
208
209 sub check_fields {
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");
214                 }
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");
219                 }
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");
224                 }
225         }
226 }
227
228 sub write_xml {
229         my @std_items = get_items();
230         my @input_fields = get_fields();
231
232         my $time = time;
233         my $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
234                  ."<address-book name=\"$new_addrbook\" >\n  ";
235
236         my $prev_line;
237
238         foreach my $line (@csvlines) {
239                 $csv->parse($line);
240                 my @fields = $csv->fields;
241                 # check if an entry contains line breaks
242                 if ($#fields != $#input_fields) {
243                         if ($prev_line) {
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;
251                                         next;
252                                 }
253                         } else {
254                                 $line =~ s/\r\n$/ /;
255                                 $line =~ s/\n$/ /;
256                                 $prev_line = $line;
257                                 next;
258                         }
259                 }
260                 $prev_line = '';
261
262                 @fields = escape_fields(@fields);
263
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    ";
269                 $time++;
270                 if ($type eq "thunderbird") {
271                         $xml .= "<address-list>\n      "
272                                ."<address uid=\"$time\" alias=\"\" "
273                                ."email=\"$fields[$std_items[4]]\" "
274                                ."remarks=\"\" />    \n";
275                         $time++;
276                         if ($fields[$std_items[5]]) {
277                                 $xml .="      <address uid=\"$time\" alias=\"\" "
278                                       ."email=\"$fields[$std_items[5]]\" "
279                                       ."remarks=\"\" />    \n";
280                         }
281                         $xml .= "    </address-list>    \n";
282                 } else {
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";
288                 }
289                 $xml .= "<attribute-list>\n";
290                 foreach my $item (@std_items) {
291                         delete($fields[$item]);
292                 }
293                 foreach my $field (0 .. $#fields) {
294                         if ($fields[$field]) { 
295                                 $time++;
296                                 $xml .= "      <attribute uid=\"$time\" "
297                                        ."name=\"$input_fields[$field]\">"
298                                        ."$fields[$field]</attribute>\n";
299                         }
300                 }
301                 $xml .= "    </attribute-list>\n  "
302                        ."</person>\n";
303                 $time++;
304         }
305
306         $xml .= "</address-book>\n";
307
308         return $xml;
309 }
310
311 sub get_items {
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');
318         }
319 }
320
321 sub get_fields {
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);
328         }
329 }
330
331 sub escape_fields {
332         my (@fields) = @_;
333
334         for (my $item = 0; $item <= $#fields; $item++) {
335                 $fields[$item] =~ s/^"//;
336                 $fields[$item] =~ s/"$//;
337                 $fields[$item] =~ s/"/&quot;/g;
338                 $fields[$item] =~ s/&/&amp;/g;
339                 $fields[$item] =~ s/'/&apos;/g;
340                 $fields[$item] =~ s/</&lt;/g;
341                 $fields[$item] =~ s/>/&gt;/g;
342         }
343         
344         return @fields;
345 }
346