0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: egroupware.py 1770 2004-11-22 06:53:49Z rogerb $ 0009 0010 """Be at one with eGroupware 0011 0012 We talk to eGroupware using its xmlrpc interface. Unfortunately the interface 0013 has several quality issues, so we try to work around them in this code. 0014 """ 0015 0016 import xmlrpclib 0017 import urlparse 0018 import time 0019 import datetime 0020 0021 def getsession(url, user, password, domain="default"): 0022 0023 # fixup whatever the user max have given us 0024 scheme, location, path, query, fragment = urlparse.urlsplit(url) 0025 0026 if scheme is None and location is None and query is None: 0027 url="http://"+url 0028 0029 if url[-1]!="/": url+="/" 0030 url+="xmlrpc.php" 0031 0032 sp=xmlrpclib.ServerProxy(url) 0033 0034 res=sp.system.login({"username": user, "password": password, "domain": domain}) 0035 0036 if "sessionid" not in res or "kp3" not in res: 0037 raise Exception("Invalid username or password") 0038 0039 scheme, location, path, query, fragment = urlparse.urlsplit(url) 0040 0041 if location.find("@")>=0: 0042 location=location[location.find("@")+1:] 0043 0044 newurl=urlparse.urlunsplit( (scheme, "%s:%s@%s" % (res["sessionid"], res["kp3"], location), path, query, fragment) ) 0045 return Session(xmlrpclib.ServerProxy(newurl), res) 0046 0047 class Session: 0048 0049 def __init__(self, sp, ifo): 0050 self.sp=sp 0051 self.__ifo=ifo 0052 0053 def __del__(self): 0054 self.sp.system.logout(self.__ifo) 0055 self.sp=None 0056 self.__ifo=None 0057 0058 def getyearcalendar(self, year): 0059 return getcalendar((year,), (year,)) 0060 0061 def getcalendar(self, start=(), end=()): 0062 if len(start)!=6 or len(end)!=6: 0063 t=time.localtime() 0064 startdefs=(t[0], 1, 1, 0,0,0) 0065 enddefs=(t[0],12,31,23,59,60) 0066 start=start+startdefs[len(start):] 0067 end=end+enddefs[len(end):] 0068 start="%04d-%02d-%02dT%02d:%02d:%02d" % start 0069 end="%04d-%02d-%02dT%02d:%02d:%02d" % end 0070 for item in self.sp.calendar.bocalendar.search({"start": start, "end": end}): 0071 for k in item.keys(): 0072 if isinstance(item[k], xmlrpclib.DateTime): 0073 v=str(item[k]) 0074 v=[int(x) for x in v[0:4], v[5:7], v[8:10], v[11:13], v[14:16], v[17:19]] 0075 if v==[0,0,0,0,0,0]: 0076 del item[k] 0077 else: 0078 item[k]=datetime.datetime(*v) 0079 yield item 0080 0081 def doescontactexist(self, id): 0082 try: 0083 return self.sp.addressbook.boaddressbook.read({'id': id}) 0084 except xmlrpclib.Fault, f: 0085 # in theory only fault 10 - Entry does not (longer) exist! 0086 # should be looked for. Unfortunately egroupware has a 0087 # bug and returns fault 9 - Access denied if the id 0088 # doesn't exist. So we consider any failure to mean 0089 # that the id doesn't exist. Reported as SF bug #1057984 0090 print "eg contact doesn't exist, fault", f 0091 return False 0092 0093 def getcontacts(self): 0094 "returns all contacts" 0095 # internally we read them a group at a time 0096 offset=0 0097 limit=5 0098 0099 # NB an offset of zero causes egroupware to return ALL contacts ignoring limit! 0100 # This has been filed as bug 1040738 at SourceForge against eGroupware. It 0101 # won't hurt unless you have huge number of contacts as egroupware will try 0102 # to return all of them at once. Alternatively make the simple fix as in 0103 # the bug report 0104 0105 while True: 0106 contacts=self.sp.addressbook.boaddressbook.search({'start': offset, 'limit': limit}) 0107 if len(contacts)==0: 0108 raise StopIteration() 0109 for i in contacts: 0110 yield i 0111 if len(contacts)<limit: 0112 raise StopIteration() 0113 offset+=len(contacts) 0114 0115 def writecontact(self, contact): 0116 "Returns the id of the contact" 0117 # egroupware returns True if the contact was written successfully using the 0118 # existing id, otherwise it returns the new id 0119 res=self.sp.addressbook.boaddressbook.write(contact) 0120 if res is True: 0121 return contact['id'] 0122 return res 0123 0124 def getcontactspbformat(self): 0125 "returns contacts in a format suitable for the BitPim phonebook importer" 0126 # eGroupware gives a nice shine in the UI, but the underlying data is 0127 # somewhat messy 0128 for c in self.getcontacts(): 0129 res={} 0130 # egroupware format is very similar to vCard 0131 res['Name']=c['fn'] 0132 res['First Name']=c['n_given'] 0133 res['Middle Name']=c['n_middle'] 0134 res['Last Name']=c['n_family'] 0135 res['UniqueSerial-id']=c['id'] 0136 res['UniqueSerial-sourcetype']='egroupware' 0137 # addresses 0138 for t,prefix in ("business", "adr_one"), ("home", "adr_two"): 0139 a={} 0140 # egroupware has street 2 and 3 in the ui, but doesn't expose them 0141 # via xmlrpc - reported as SF bug # 1043862 0142 for p2,k in ("_street", "street"), ("_locality", "city"), ("_region", "state"), \ 0143 ("_postalcode", "postalcode"), ("_countryname", "country"): 0144 if len(c.get(prefix+p2,"")): a[k]=c[prefix+p2] 0145 if t=="business" and len(c.get("org_name", "")): a['company']=c["org_name"] 0146 if len(a): 0147 a['type']=t 0148 aa="Address" 0149 if aa in res: 0150 aa+="2" 0151 assert aa not in res 0152 res[aa]=a 0153 # categories 0154 cats=[] 0155 ccats=c.get("cat_id", "") 0156 if len(ccats): # could be empty string or a dict 0157 for cat in ccats: 0158 cats.append(ccats[cat]) 0159 if len(cats): 0160 res["Categories"]=cats 0161 # email - we ignore the silly egroupware email type field 0162 suf="" 0163 if len(c.get("email","")): 0164 res["Email Address"]={'email': c['email'], 'type': 'business'} 0165 suf="2" 0166 if len(c.get("email_home", "")): 0167 res["Email Address"+suf]={'email': c['email_home'], 'type': 'home'} 0168 # phone numbers 0169 res["Home Phone"]=c['tel_home'] 0170 res["Mobile Phone"]=c['tel_cell'] # nb: in eGroupware this is business cell 0171 res["Business Fax"]=c['tel_fax'] 0172 res["Pager"]=c['tel_pager'] # nb: in eGroupware this is business pager 0173 res["Business Phone"]=c['tel_work'] 0174 # various other fields 0175 res['Notes']=c['note'] 0176 res['Business Web Page']=c['url'] 0177 0178 # filter out empty fields 0179 res=dict([(k,v) for k,v in res.items() if len(v)]) 0180 yield res 0181 0182 def getcategories(self): 0183 "Get the list of categories" 0184 # egroupware returns a dict with each key being an asciized integer 0185 # id. The same field is present in the dict value, so we just return 0186 # the values 0187 return [v for k,v in self.sp.addressbook.boaddressbook.categories(True).items()] 0188 0189 0190 if __name__=='__main__': 0191 import sys 0192 import common 0193 s=getsession(*sys.argv[1:]) 0194 for v in s.getcategories(): 0195 print common.prettyprintdict(v) 0196 #for n,i in enumerate(s.getcontacts()): 0197 # print n,common.prettyprintdict(i) 0198 0199 #print n,i.get('id',""),i.get('n_given', ""),i.get('n_family', "") 0200 0201
Generated by PyXR 0.9.4