3.9.2 unleashed
[claws.git] / tools / vcard2xml.py
1 #!/usr/bin/env python
2 # -*- coding: latin-1 -*-
3 """
4
5 Copyright © 2003 Bogdan Sumanariu <zarrok@yahoo.com>
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   script name : evolutionvcard2claws.py
22
23  script purpose : convert an evolution addressbook VCARD file 
24  into a Claws Mail addressbook
25
26  tested with evolution 1.2.x, and 1.4.x
27
28 """
29
30 import string
31 import sys
32 import time
33 import os
34 import StringIO
35
36 keywds = ('x-evolution-file-as','fn', 'n','email;internet','nickname', 'url', 'org')
37
38 def normalizeLongLines(file):
39         """
40         Skip line breaks after 72 chars
41         """
42         buf = ''
43         
44         line = file.readline()
45         while line:
46                 if line[0] == ' ':
47                         buf = buf.rstrip('\n')
48                         line = line.lstrip();
49                         buf += line
50                 else:
51                         buf += line
52                 line = file.readline()
53         
54         return buf
55
56 def getEmailAddress(vcard):
57         """
58         Get email address.
59         Supported formats:
60                 - email;something
61                 - email;type=something
62         something := (internet,work,home, other)
63         """
64         
65         for key in vcard:
66                 items = key.split(';')
67                 if len(items) == 2:
68                         if items[0].lower() == 'email':
69                                 list = vcard[key]
70                                 return list[0]
71                 else:
72                         if key.lower() == 'email':
73                                 list = vcard[key]
74                                 return list[0]
75         
76         return ""
77
78 def findName(vcard):
79         """
80         Find a version 3.0 name
81         """
82         for key in vcard:
83                 items = key.split(';')
84                 if len(items) == 2:
85                         if items[0].lower() == 'n':
86                                 return  vcard[key]
87                 else:
88                         if key.lower() == 'n':
89                                 return  vcard[key]
90         
91         return None
92
93 ################################################################################
94 ##  reads a vcard and stores as hash pairs key/value where value is a list    ##
95 ################################################################################
96
97 def readVCARD (buffer) :
98
99         """
100
101         skips fom <file> until a 'begin' tag from VCARD is encountered.
102         from this point starts constructing a map (key, [values] ) 
103         VCARD entry format -> tag:value 
104         
105                 key <- tag
106                 [values] <- list with the values of <tag> if there are more tags with the same name
107
108         """
109         r=' '
110         bgn,end = -1, -1;
111         d = dict()
112         while r and bgn < 0 :
113                 r = buffer.readline()
114                 if len (r)  == 0 : return dict()
115                 if string.find('begin',string.lower(string.strip(r))) :
116                         bgn = 1
117         while r and end < 0 :
118                 r = buffer.readline()
119                 s = string.split(string.lower(string.strip(r)),':')
120                 if s[0] <> '' :
121                         if d.has_key(s[0]) :
122                                 d[s[0]].append(s[1])
123                         elif len(s) > 1:
124                                 d[s[0]] = [s[1]]        
125                         else :
126                                 d[s[0]] = ['']
127                         if s[0] == 'end' : end = 1      
128         return d
129
130 ##################################################################################
131
132 ###############################################################################################
133 ## writes on a given file an xml representation for claws-mail addressbook received as a hash  ##
134 ###############################################################################################
135
136 def writeXMLREPR (vcard,file,uid) :
137
138         """
139         based on <vcard> and <uid> writes only recognized tags (the ones defined in <keywds> list)
140         NOTE: <url> and <org> tag will be written as attributes (there are such tags in claws-mail's
141               XML schema)
142         """
143         if len (vcard.keys()) == 0 : return
144         item = vcard.get(keywds[2]);
145         if item:
146                 name = string.split(item[0],';')
147         else:
148                 """ version 3.0 n ?"""
149                 name = findName(vcard)
150                 if not name:
151                         return
152
153         fn, ln, nick, cn, a = '', '', '', '', ''
154
155         if len(name) >= 2 :
156                 fn = name[0]
157                 ln = name[1]
158         elif len(name) ==1 :
159                 fn = name[0]
160         
161         if vcard.has_key(keywds[4]) :
162                 nick = vcard.get(keywds[4])[0]
163         if len(vcard.get(keywds[1])[0]) :
164                 cn = vcard.get(keywds[1])[0]
165         else :
166                 cn = vcard.get(keywds[0])[0];
167
168         a += str('\n<person uid=\"' + str(uid[0]) + '\" first-name=\"' + fn + '\" last-name=\"' + ln
169                 + '\" nick-name=\"' + nick + '\" cn=\"' + cn + '\" >\n')
170         a += '\t<address-list>\n'
171         if vcard.get(keywds[3]) :
172                 for c in vcard.get(keywds[3]) :
173                         uid[0] = uid[0] + 1
174                         a += '\t\t<address uid=\"' + str(uid[0]) + '\" alias=\"' + nick  + '\" email=\"' + c + '\" remarks=\"\" />\n'
175         else :
176                 email = getEmailAddress(vcard)
177                 uid[0] = uid[0]+1
178                 a += '\t\t<address uid=\"' + str(uid[0]) + '\" alias=\"' +  nick + '\" email=\"' + email + '\" remarks=\"\" />\n'
179         a += '\t</address-list>\n'
180         a += '\t<attribute-list>\n'
181         for key in keywds[5:] :
182                 if vcard.get(key) :
183                         for c in vcard.get(key) :
184                                 uid[0] = uid[0] + 1
185                                 a += '\t\t<attribute uid=\"' + str(uid[0]) + '\" name=\"' + key +'\">'+c+'</attribute>\n'
186         a += '\t</attribute-list>\n'
187         a += '</person>\n'
188         file.write(a)
189         file.flush()
190                 
191 ###################################################################################################
192
193 def convert (in_f, o_f, name='INBOX') :
194         d = {'d':1}
195         uid = [int(time.time())]
196         
197         try :
198                 print 'proccessing...\n'
199                 o_f.write('<?xml version="1.0" encoding="ISO-8859-1" ?>\n<address-book name="'+name+'" >\n');
200                 
201                 buf = normalizeLongLines(in_f)
202                 buffer = StringIO.StringIO(buf)
203                 while len(d.keys()) > 0 :
204                         d = readVCARD(buffer)
205                         writeXMLREPR (d, o_f, uid)
206                         uid[0] = uid [0]+1
207                 
208                 o_f.write('\n</address-book>')
209                 print 'finished processing...\n'
210         except IOError, err :
211                 print 'Caught an IOError : ',err,'\t ABORTING!!!'
212                 raise err
213
214 #################################################################################################
215
216 def execute () :
217         if len(sys.argv) <> 3 and len(sys.argv) <> 2 :
218                 print str("\nUsage: vcard2xml.py  source_file [destination_file]\n\n" +
219                 '\tWhen only <source_file> is specified will overwrite the existing addressbook.\n'+
220                 '\tWhen both arguments are suplied will create a new additional addressbook named \n\tas the destination file.'+'\n\tNOTE: in both cases the Claws Mail must be closed and ran at least once.\n\n')
221                 sys.exit(1)
222
223         in_file = None
224         out_file = None
225         path_to_out = os.environ['HOME']+'/.claws-mail/'
226         adr_idx = 'addrbook--index.xml'
227         adr_idx_file = None
228         tmp_adr_idx_file= None
229         got_ex = 0
230
231         try :
232                 in_file = open(sys.argv[1])
233         except IOError, e:
234                 print 'Could not open input file <',sys.argv[1],'>  ABORTING'
235                 sys.exit(1)
236
237         if len(sys.argv) == 2 :
238                 try :
239                         dlist = os.listdir(path_to_out);
240                         flist=[]
241                         for l in dlist :
242                                 if l.find('addrbook') == 0 and l.find("addrbook--index.xml") < 0 and l.find('bak') < 0 :
243                                         flist.append(l)
244                         flist.sort()
245                         out_file = flist.pop()
246                         os.rename(path_to_out+out_file, path_to_out+out_file+'.tmp')
247                         out_file = open(path_to_out+out_file,'w')
248                         convert(in_file, out_file)
249                 except Exception, e:
250                         got_ex = 1
251                         print 'got exception: ', e
252         else :
253                 try :
254                         os.rename(path_to_out+adr_idx, path_to_out+adr_idx+'.tmp')
255                         tmp_adr_idx_file = open(path_to_out+adr_idx+'.tmp')
256                         adr_idx_file = open(path_to_out+adr_idx,'w')
257                 except Exception, e :
258                         print 'Could not open <', path_to_out+adr_idx,'> file. Make sure you started Claws Mail at least once.'
259                         sys.exit(1)
260                 try :
261                         out_file = open(path_to_out+sys.argv[2],'w')
262                         convert(in_file, out_file, sys.argv[2].split('.xml')[0])
263                         l = tmp_adr_idx_file.readline()
264                         while l :
265                                 if l.strip() == '</book_list>' :
266                                         adr_idx_file.write('\t<book name="'+sys.argv[2].split('.xml')[0] +'" file="'+sys.argv[2]+'" />\n')
267                                         adr_idx_file.write(l)
268                                 else :
269                                         adr_idx_file.write(l)
270                                 l = tmp_adr_idx_file.readline()
271                 except Exception, e:
272                         got_ex = 1
273                         print 'got exception: ', e
274         
275
276         if got_ex :
277                 #clean up the mess
278                 print 'got exception, cleaning up the mess... changed files will be restored...\n'
279                 if adr_idx_file :
280                         adr_idx_file.close()
281                 if out_file :
282                         out_file.close()
283                 if len(sys.argv) == 2 :
284                         os.rename(out_file.name+'.tmp', out_file.name)
285                 else :
286                         os.remove(out_file.name)
287                         os.rename(path_to_out+adr_idx+'.tmp', path_to_out+adr_idx)
288                 if tmp_adr_idx_file :
289                         tmp_adr_idx_file.close()
290                                 
291         else :
292                 #closing all and moving temporary data into place
293                 print 'closing open files...\n'
294                 in_file.close()
295                 out_file.close()        
296                 if len(sys.argv) == 3 :
297                         os.rename(path_to_out+adr_idx+'.tmp',path_to_out+adr_idx+'.bak' )
298                 if len(sys.argv) == 2 :
299                         os.rename(out_file.name+'.tmp', out_file.name+'.bak')
300                 if adr_idx_file :
301                         adr_idx_file.close()
302                 if tmp_adr_idx_file :
303                         tmp_adr_idx_file.close()
304                 print 'done!'
305                 
306
307 if __name__ == '__main__':
308     execute ()
309
310