update donations URI
[claws.git] / tools / vcard2xml.py
blob473c85b6186f4692d08ed2a83ca202fe15d83b7a
1 #!/usr/bin/env python3
2 # -*- coding: latin-1 -*-
3 """
5 Copyright © 2003 Bogdan Sumanariu <zarrok@yahoo.com>
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 script name : evolutionvcard2claws.py
23 script purpose : convert an evolution addressbook VCARD file
24 into a Claws Mail addressbook
26 tested with evolution 1.2.x, and 1.4.x
28 """
30 import string
31 import sys
32 import time
33 import os
34 from io import StringIO
36 keywds = ('x-evolution-file-as','fn', 'n','email;internet','nickname', 'url', 'org')
38 def normalizeLongLines(file):
39 """
40 Skip line breaks after 72 chars
41 """
42 buf = ''
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()
54 return buf
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 """
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]
76 return ""
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]
91 return None
93 ################################################################################
94 ## reads a vcard and stores as hash pairs key/value where value is a list ##
95 ################################################################################
97 def readVCARD (buffer) :
99 """
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
105 key <- tag
106 [values] <- list with the values of <tag> if there are more tags with the same name
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 r.strip().lower().find('begin') :
116 bgn = 1
117 while r and end < 0 :
118 r = buffer.readline()
119 s = r.strip().lower().split(':')
120 if s[0] != '' :
121 if s[0] in d :
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
130 ##################################################################################
132 ###############################################################################################
133 ## writes on a given file an xml representation for claws-mail addressbook received as a hash ##
134 ###############################################################################################
136 def writeXMLREPR (vcard,file,uid) :
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)
143 if len (vcard.keys()) == 0 : return
144 item = vcard.get(keywds[2]);
145 if item:
146 name = item[0].split(';')
147 else:
148 """ version 3.0 n ?"""
149 name = findName(vcard)
150 if not name:
151 return
153 fn, ln, nick, cn, a = '', '', '', '', ''
155 if len(name) >= 2 :
156 fn = name[0]
157 ln = name[1]
158 elif len(name) ==1 :
159 fn = name[0]
161 if keywds[4] in vcard :
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];
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()
191 ###################################################################################################
193 def convert (in_f, o_f, name='INBOX') :
194 d = {'d':1}
195 uid = [int(time.time())]
197 try :
198 print('proccessing...\n')
199 o_f.write('<?xml version="1.0" encoding="ISO-8859-1" ?>\n<address-book name="'+name+'" >\n');
201 buf = normalizeLongLines(in_f)
202 buffer = StringIO(buf)
203 while len(d.keys()) > 0 :
204 d = readVCARD(buffer)
205 writeXMLREPR (d, o_f, uid)
206 uid[0] = uid [0]+1
208 o_f.write('\n</address-book>')
209 print('finished processing...\n')
210 except IOError as err :
211 print('Caught an IOError : ',err,'\t ABORTING!!!')
212 raise err
214 #################################################################################################
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)
223 in_file = None
224 out_file = None
225 path_to_out = os.environ['HOME']+'/.claws-mail/addrbook/'
226 adr_idx = 'addrbook--index.xml'
227 adr_idx_file = None
228 tmp_adr_idx_file= None
229 got_ex = 0
231 try :
232 in_file = open(sys.argv[1])
233 except IOError as e:
234 print('Could not open input file <',sys.argv[1],'> ABORTING')
235 sys.exit(1)
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 as 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 as 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 as e:
272 got_ex = 1
273 print('got exception: ', e)
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()
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!')
307 if __name__ == '__main__':
308 execute ()