0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004 Roger Binns <rogerb@rogerbinns.com> 0004 ### Copyright (C) 2004 Peter Pletcher <peterpl@pacbell.net> 0005 ### 0006 ### This program is free software; you can redistribute it and/or modify 0007 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0008 ### 0009 ### $Id: evolution.py 1442 2004-07-14 05:14:24Z rogerb $ 0010 0011 "Be at one with Evolution" 0012 0013 # Evolution mostly sucks when compared to Outlook. The UI and functionality 0014 # for the address book is a literal copy. There is no API as such and we 0015 # just have to delve around the filesystem 0016 0017 # root directory is ~/evolution 0018 # folders are any directory containing a file named folder-metadata.xml 0019 # note that folders can be nested 0020 # 0021 # the folder name is the directory name. The folder-metadata.xml file 0022 # does contain description tag, but it isn't normally displayed and 0023 # is usually empty for user created folders 0024 # 0025 # if the folder contains any addressbook entries, then there will 0026 # be an addressbook.db file 0027 # 0028 # the file should be opened using bsddb 0029 # import bsddb 0030 # db=bsddb.hashopen("addressbook.db", "r") 0031 # db.keys() lists keys, db[key] gets item 0032 # 0033 # the item contains exactly one field which is a null terminated string 0034 # containing a vcard 0035 0036 0037 import sys 0038 import os 0039 import re 0040 0041 if sys.platform!="linux2": 0042 raise ImportError() 0043 0044 try: 0045 import bsddb 0046 except: 0047 raise ImportError() 0048 0049 0050 userdir=os.path.expanduser("~") 0051 evolutionpath="evolution/local" 0052 evolutionbasedir=os.path.join(userdir, evolutionpath) 0053 evolutionexporter = { 0054 'command' : "evolution-addressbook-export", 0055 'folderid' : "<Evolution Addressbook Exporter>", 0056 'name' : "<Evolution Addressbook Exporter>", 0057 'type' : ["address book"] 0058 } 0059 0060 def getcontacts(folder): 0061 """Returns the contacts as a list of string vcards 0062 0063 Note that the Windows EOL convention is used""" 0064 0065 if folder == evolutionexporter['folderid']: 0066 return getcontacts_evoexporter() 0067 0068 dir=os.path.expanduser(folder) 0069 p=os.path.join(dir, "addressbook.db") 0070 if not os.path.isfile(p): 0071 # ok, this is not an address book folder 0072 if not os.path.isfile(os.path.join(dir, "folder-metadata.xml")): 0073 raise ValueError("Supplied folder is not a folder! "+folder) 0074 raise ValueError("Folder does not contain contacts! "+folder) 0075 res=[] 0076 db=bsddb.hashopen(p, 'r') 0077 for key in db.keys(): 0078 if key.startswith("PAS-DB-VERSION"): # no need for this field 0079 continue 0080 data=db[key] 0081 while data[-1]=="\x00": # often has actual null on the end 0082 data=data[:-1] 0083 res.append(data) 0084 db.close() 0085 return res 0086 0087 class EvolutionExportException(Exception): 0088 pass 0089 0090 def getcontacts_evoexporter(): 0091 """Get the cards by running evolution-addressbook-export 0092 0093 Note that this code returns all the contacts as a single 0094 string item. It seemed silly to split them apart when 0095 the caller then just puts them back together as one 0096 string""" 0097 evo_export = os.popen(evolutionexporter['command']) 0098 evo_cards = evo_export.read() 0099 evo_export_status = evo_export.close() 0100 if evo_export_status is not None: 0101 raise EvolutionExportException("%s failed with code %s" % (evolutionexporter['command'], `evo_export_status`)) 0102 return [evo_cards] 0103 0104 def getfsfolders(basedir=evolutionbasedir): 0105 0106 res={} 0107 children=[] 0108 0109 for f in os.listdir(basedir): 0110 p=os.path.join(basedir, f) 0111 0112 # deal with child folders (depth first) 0113 if os.path.isdir(p): 0114 f=getfsfolders(p) 0115 if len(f): 0116 children.extend(f) 0117 continue 0118 0119 # if we have any children, sort them 0120 if len(children): 0121 lc=[ (child['name'], child) for child in children] # decorate 0122 lc.sort() # sort 0123 children=[child for _, child in lc] # un-decorate 0124 0125 0126 # do we have a meta-data file? 0127 if not os.path.isfile(os.path.join(basedir, "folder-metadata.xml")): 0128 return children 0129 0130 # ok, what type is this folder 0131 t=[] 0132 for file,type in ( ("mbox", "mailbox"), ("calendar.ics", "calendar"), ("addressbook.db", "address book"), 0133 ("tasks.ics", "tasks") ): 0134 if os.path.isfile(os.path.join(basedir, file)): 0135 t.append(type) 0136 0137 entry={} 0138 entry['dirpath']=basedir 0139 entry['name']=os.path.basename(basedir) 0140 entry['folderid']=basedir.replace(userdir, "~", 1) 0141 entry['type']=t 0142 if len(children): 0143 entry['children']=children 0144 # tell children who their daddy is 0145 for c in children: 0146 c['parent']=entry 0147 0148 return [entry] 0149 0150 def getspecialfolders(): 0151 "Return a list of any special folders" 0152 0153 # the only one we look for currently is evolution-addressbook-export 0154 # command 0155 evo_version = os.popen(evolutionexporter['command'] + " --version") 0156 evo_version_result = evo_version.read() 0157 evo_version_status = evo_version.close() 0158 if evo_version_status is not None: 0159 return [] 0160 # it doesn't work with earlier versions of evolution, so we do a version 0161 # check 0162 if evo_version_result.startswith("Gnome evolution 1.4"): 0163 return [evolutionexporter] 0164 else: 0165 return [] 0166 0167 def getfolders(): 0168 return getspecialfolders()+getfsfolders() 0169 0170 def pickfolder(selectedid=None, parent=None, title="Select Evolution Folder"): 0171 # we do the imports etc in the function so that this file won't 0172 # require gui code unless this function is called 0173 0174 import wx 0175 import wx.gizmos 0176 0177 dlg=wx.Dialog(parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, size=(450,350)) 0178 vbs=wx.BoxSizer(wx.VERTICAL) 0179 0180 tl=wx.gizmos.TreeListCtrl(dlg, -1, style=wx.TR_DEFAULT_STYLE|wx.TR_HIDE_ROOT) 0181 0182 tl.AddColumn("Name") 0183 tl.SetMainColumn(0) 0184 tl.AddColumn("Type") 0185 tl.SetColumnWidth(0, 300) 0186 0187 0188 0189 def addnode(parent, item, selected): 0190 node=tl.AppendItem(parent, item['name']) 0191 if selected==item['folderid']: 0192 tl.SelectItem(node) 0193 tl.SetItemText(node, ", ".join(item['type']), 1) 0194 tl.SetPyData(node, item) 0195 if item.has_key("children"): 0196 for child in item['children']: 0197 addnode(node, child, selected) 0198 tl.Expand(node) 0199 0200 root=tl.AddRoot("?") 0201 tl.SetPyData(root, None) 0202 0203 for f in getfolders(): 0204 addnode(root, f, selectedid) 0205 0206 # select first folder if nothing is selected 0207 if tl.GetPyData(tl.GetSelection()) is None: 0208 child,_=tl.GetFirstChild(root, 1234) 0209 tl.SelectItem(child) 0210 0211 vbs.Add(tl, 1, wx.EXPAND|wx.ALL, 5) 0212 0213 vbs.Add(wx.StaticLine(dlg, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 0214 0215 vbs.Add(dlg.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5) 0216 0217 dlg.SetSizer(vbs) 0218 dlg.SetAutoLayout(True) 0219 0220 if dlg.ShowModal()==wx.ID_OK: 0221 folderid=tl.GetPyData(tl.GetSelection())['folderid'] 0222 else: 0223 folderid=None 0224 dlg.Destroy() 0225 return folderid 0226 0227 # we use a pathname like "~/evolution/local/Contacts" as the folder id 0228 # and the same as a "folder" 0229 0230 class Match: 0231 def __init__(self, folder): 0232 self.folder=folder 0233 0234 def getfolderfromid(id, default=False): 0235 "Return a folder object given the id" 0236 0237 f=_findfolder(id) 0238 if f is not None: 0239 return f['folderid'] 0240 0241 if default: 0242 # look for a default 0243 for f in getfolders(): 0244 if "address book" in f['type']: 0245 return f["folderid"] 0246 # brute force 0247 return getfolders()[0]['folderid'] 0248 0249 return None 0250 0251 def __findfolder(node, id): 0252 "Recursive function to locate a folder, using Match exception to return the found folder" 0253 if node['folderid']==id: 0254 raise Match(node) 0255 for c in node.get("children", []): 0256 __findfolder(c, id) 0257 0258 def _findfolder(id): 0259 for f in getfolders(): 0260 try: 0261 __findfolder(f, id) 0262 except Match,m: 0263 return m.folder # we found it 0264 return None 0265 0266 0267 def getfoldername(id): 0268 f=_findfolder(id) 0269 if f is None: 0270 raise AttributeError("No such folder "+id) 0271 0272 n=[] 0273 while f: 0274 n=[f['name']]+n 0275 f=f.get('parent',None) 0276 0277 return " / ".join(n) 0278 0279 def getfolderid(folder): 0280 return folder 0281 0282 0283 if __name__=="__main__": 0284 # a folder selector 0285 import wx 0286 import wx.gizmos 0287 0288 app=wx.PySimpleApp() 0289 0290 folder=pickfolder() 0291 print folder 0292 0293 print "\n".join(getcontacts(folder)) 0294 0295 0296
Generated by PyXR 0.9.4