vcard2xml.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #!/usr/bin/env python
  2. # -*- coding: latin-1 -*-
  3. """
  4. Copyright © 2003 Bogdan Sumanariu <zarrok@yahoo.com>
  5. This file is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful, but
  10. WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. script name : evolutionvcard2claws.py
  17. script purpose : convert an evolution addressbook VCARD file
  18. into a Claws Mail addressbook
  19. tested with evolution 1.2.x, and 1.4.x
  20. """
  21. import string
  22. import sys
  23. import time
  24. import os
  25. import StringIO
  26. keywds = ('x-evolution-file-as','fn', 'n','email;internet','nickname', 'url', 'org')
  27. def normalizeLongLines(file):
  28. """
  29. Skip line breaks after 72 chars
  30. """
  31. buf = ''
  32. line = file.readline()
  33. while line:
  34. if line[0] == ' ':
  35. buf = buf.rstrip('\n')
  36. line = line.lstrip();
  37. buf += line
  38. else:
  39. buf += line
  40. line = file.readline()
  41. return buf
  42. def getEmailAddress(vcard):
  43. """
  44. Get email address.
  45. Supported formats:
  46. - email;something
  47. - email;type=something
  48. something := (internet,work,home, other)
  49. """
  50. for key in vcard:
  51. items = key.split(';')
  52. if len(items) == 2:
  53. if items[0].lower() == 'email':
  54. list = vcard[key]
  55. return list[0]
  56. else:
  57. if key.lower() == 'email':
  58. list = vcard[key]
  59. return list[0]
  60. return ""
  61. def findName(vcard):
  62. """
  63. Find a version 3.0 name
  64. """
  65. for key in vcard:
  66. items = key.split(';')
  67. if len(items) == 2:
  68. if items[0].lower() == 'n':
  69. return vcard[key]
  70. else:
  71. if key.lower() == 'n':
  72. return vcard[key]
  73. return None
  74. ################################################################################
  75. ## reads a vcard and stores as hash pairs key/value where value is a list ##
  76. ################################################################################
  77. def readVCARD (buffer) :
  78. """
  79. skips fom <file> until a 'begin' tag from VCARD is encountered.
  80. from this point starts constructing a map (key, [values] )
  81. VCARD entry format -> tag:value
  82. key <- tag
  83. [values] <- list with the values of <tag> if there are more tags with the same name
  84. """
  85. r=' '
  86. bgn,end = -1, -1;
  87. d = dict()
  88. while r and bgn < 0 :
  89. r = buffer.readline()
  90. if len (r) == 0 : return dict()
  91. if string.find('begin',string.lower(string.strip(r))) :
  92. bgn = 1
  93. while r and end < 0 :
  94. r = buffer.readline()
  95. s = string.split(string.lower(string.strip(r)),':')
  96. if s[0] <> '' :
  97. if d.has_key(s[0]) :
  98. d[s[0]].append(s[1])
  99. elif len(s) > 1:
  100. d[s[0]] = [s[1]]
  101. else :
  102. d[s[0]] = ['']
  103. if s[0] == 'end' : end = 1
  104. return d
  105. ##################################################################################
  106. ###############################################################################################
  107. ## writes on a given file an xml representation for claws-mail addressbook received as a hash ##
  108. ###############################################################################################
  109. def writeXMLREPR (vcard,file,uid) :
  110. """
  111. based on <vcard> and <uid> writes only recognized tags (the ones defined in <keywds> list)
  112. NOTE: <url> and <org> tag will be written as attributes (there are such tags in claws-mail's
  113. XML schema)
  114. """
  115. if len (vcard.keys()) == 0 : return
  116. item = vcard.get(keywds[2]);
  117. if item:
  118. name = string.split(item[0],';')
  119. else:
  120. """ version 3.0 n ?"""
  121. name = findName(vcard)
  122. if not name:
  123. return
  124. fn, ln, nick, cn, a = '', '', '', '', ''
  125. if len(name) >= 2 :
  126. fn = name[0]
  127. ln = name[1]
  128. elif len(name) ==1 :
  129. fn = name[0]
  130. if vcard.has_key(keywds[4]) :
  131. nick = vcard.get(keywds[4])[0]
  132. if len(vcard.get(keywds[1])[0]) :
  133. cn = vcard.get(keywds[1])[0]
  134. else :
  135. cn = vcard.get(keywds[0])[0];
  136. a += str('\n<person uid=\"' + str(uid[0]) + '\" first-name=\"' + fn + '\" last-name=\"' + ln
  137. + '\" nick-name=\"' + nick + '\" cn=\"' + cn + '\" >\n')
  138. a += '\t<address-list>\n'
  139. if vcard.get(keywds[3]) :
  140. for c in vcard.get(keywds[3]) :
  141. uid[0] = uid[0] + 1
  142. a += '\t\t<address uid=\"' + str(uid[0]) + '\" alias=\"' + nick + '\" email=\"' + c + '\" remarks=\"\" />\n'
  143. else :
  144. email = getEmailAddress(vcard)
  145. uid[0] = uid[0]+1
  146. a += '\t\t<address uid=\"' + str(uid[0]) + '\" alias=\"' + nick + '\" email=\"' + email + '\" remarks=\"\" />\n'
  147. a += '\t</address-list>\n'
  148. a += '\t<attribute-list>\n'
  149. for key in keywds[5:] :
  150. if vcard.get(key) :
  151. for c in vcard.get(key) :
  152. uid[0] = uid[0] + 1
  153. a += '\t\t<attribute uid=\"' + str(uid[0]) + '\" name=\"' + key +'\">'+c+'</attribute>\n'
  154. a += '\t</attribute-list>\n'
  155. a += '</person>\n'
  156. file.write(a)
  157. file.flush()
  158. ###################################################################################################
  159. def convert (in_f, o_f, name='INBOX') :
  160. d = {'d':1}
  161. uid = [int(time.time())]
  162. try :
  163. print 'proccessing...\n'
  164. o_f.write('<?xml version="1.0" encoding="ISO-8859-1" ?>\n<address-book name="'+name+'" >\n');
  165. buf = normalizeLongLines(in_f)
  166. buffer = StringIO.StringIO(buf)
  167. while len(d.keys()) > 0 :
  168. d = readVCARD(buffer)
  169. writeXMLREPR (d, o_f, uid)
  170. uid[0] = uid [0]+1
  171. o_f.write('\n</address-book>')
  172. print 'finished processing...\n'
  173. except IOError, err :
  174. print 'Caught an IOError : ',err,'\t ABORTING!!!'
  175. raise err
  176. #################################################################################################
  177. def execute () :
  178. if len(sys.argv) <> 3 and len(sys.argv) <> 2 :
  179. print str("\nUsage: vcard2xml.py source_file [destination_file]\n\n" +
  180. '\tWhen only <source_file> is specified will overwrite the existing addressbook.\n'+
  181. '\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')
  182. sys.exit(1)
  183. in_file = None
  184. out_file = None
  185. path_to_out = os.environ['HOME']+'/.claws-mail/'
  186. adr_idx = 'addrbook--index.xml'
  187. adr_idx_file = None
  188. tmp_adr_idx_file= None
  189. got_ex = 0
  190. try :
  191. in_file = open(sys.argv[1])
  192. except IOError, e:
  193. print 'Could not open input file <',sys.argv[1],'> ABORTING'
  194. sys.exit(1)
  195. if len(sys.argv) == 2 :
  196. try :
  197. dlist = os.listdir(path_to_out);
  198. flist=[]
  199. for l in dlist :
  200. if l.find('addrbook') == 0 and l.find("addrbook--index.xml") < 0 and l.find('bak') < 0 :
  201. flist.append(l)
  202. flist.sort()
  203. out_file = flist.pop()
  204. os.rename(path_to_out+out_file, path_to_out+out_file+'.tmp')
  205. out_file = open(path_to_out+out_file,'w')
  206. convert(in_file, out_file)
  207. except Exception, e:
  208. got_ex = 1
  209. print 'got exception: ', e
  210. else :
  211. try :
  212. os.rename(path_to_out+adr_idx, path_to_out+adr_idx+'.tmp')
  213. tmp_adr_idx_file = open(path_to_out+adr_idx+'.tmp')
  214. adr_idx_file = open(path_to_out+adr_idx,'w')
  215. except Exception, e :
  216. print 'Could not open <', path_to_out+adr_idx,'> file. Make sure you started Claws Mail at least once.'
  217. sys.exit(1)
  218. try :
  219. out_file = open(path_to_out+sys.argv[2],'w')
  220. convert(in_file, out_file, sys.argv[2].split('.xml')[0])
  221. l = tmp_adr_idx_file.readline()
  222. while l :
  223. if l.strip() == '</book_list>' :
  224. adr_idx_file.write('\t<book name="'+sys.argv[2].split('.xml')[0] +'" file="'+sys.argv[2]+'" />\n')
  225. adr_idx_file.write(l)
  226. else :
  227. adr_idx_file.write(l)
  228. l = tmp_adr_idx_file.readline()
  229. except Exception, e:
  230. got_ex = 1
  231. print 'got exception: ', e
  232. if got_ex :
  233. #clean up the mess
  234. print 'got exception, cleaning up the mess... changed files will be restored...\n'
  235. if adr_idx_file :
  236. adr_idx_file.close()
  237. if out_file :
  238. out_file.close()
  239. if len(sys.argv) == 2 :
  240. os.rename(out_file.name+'.tmp', out_file.name)
  241. else :
  242. os.remove(out_file.name)
  243. os.rename(path_to_out+adr_idx+'.tmp', path_to_out+adr_idx)
  244. if tmp_adr_idx_file :
  245. tmp_adr_idx_file.close()
  246. else :
  247. #closing all and moving temporary data into place
  248. print 'closing open files...\n'
  249. in_file.close()
  250. out_file.close()
  251. if len(sys.argv) == 3 :
  252. os.rename(path_to_out+adr_idx+'.tmp',path_to_out+adr_idx+'.bak' )
  253. if len(sys.argv) == 2 :
  254. os.rename(out_file.name+'.tmp', out_file.name+'.bak')
  255. if adr_idx_file :
  256. adr_idx_file.close()
  257. if tmp_adr_idx_file :
  258. tmp_adr_idx_file.close()
  259. print 'done!'
  260. if __name__ == '__main__':
  261. execute ()