#!/usr/bin/env python ''' LDIF of an inetOrgPerson to vCard. Read an LDIF file of inetOrgPerson LDAP schema entries into vCard format. This is not a full implementation of inetOrgPerson, I needed some results in a hurry and only implemented what I really needed for this case. I've tried to use python vobject, but it ended being more complicated than generating my own vCard. It requires python-ldap. It takes two arguments, the existing and the new file. ''' import ldif class vCard(object): def __init__(self, iop_entry): self._content = ['BEGIN:VCARD\n', 'VERSION:3.0\n', 'END:VCARD\n'] self._v_givenname = '' self._v_sn = '' self._v_streetaddress = '' self._v_l = '' self._v_st = '' self._v_postalcode = '' for k, v in iop_entry.items(): if k == 'ou' or k == 'dc' or k == 'objectclass' or k == 'dn': continue else: try: f = getattr(self, '_' + k) except AttributeError: print 'No method defined for attribute "' + k + '".' else: f(v) self._finish() def value(self): return self._content def _cn(self, value): v = 'FN;CHARSET=UTF-8:' + value[0] + '\n' self._content.insert(-1, v) def _givenname(self, value): self._v_givenname = ';' + value[0] def _sn(self, value): self._v_sn = value[0] def _streetaddress(self, value): self._v_streetaddress = ';' + value[0] def _l(self, value): self._v_l = ';' + value[0] def _st(self, value): self._v_st = ';' + value[0] def _postalcode(self, value): self._v_postalcode = ';' + value[0] def _mobile(self, value): v = 'TEL;TYPE=CELL,VOICE:' + value[0] + '\n' self._content.insert(-1, v) def _mobiletelephonenumber(self, value): self._mobile(value) def _fax(self, value): v = 'TEL;TYPE=FAX:' + value[0] + '\n' self._content.insert(-1, v) def _facsimiletelephonenumber(self, value): self._fax(value) def _organizationname(self, value): v = 'ORG:;CHARSET=UTF-8:' + value[0] + '\n' self._content.insert(-1, v) def _mail(self, value): v = 'EMAIL;TYPE=INTERNET:' + value[0] + '\n' self._content.insert(-1, v) def _rfc822mailbox(self, value): self._mail(value) def _telephonenumber(self, value): v = 'TEL;TYPE=WORK,VOICE:' + value[0] + '\n' self._content.insert(-1, v) def _hometelephonenumber(self, value): v = 'TEL;TYPE=HOME,VOICE:' + value[0] + '\n' self._content.insert(-1, v) def _labeleduri(self, value): v = 'URL:' + value[0] + '\n' self._content.insert(-1, v) def _title(self, value): v = 'TITLE:' + value[0] + '\n' self._content.insert(-1, v) def _finish(self): # N name = 'N;CHARSET=UTF-8:' + self._v_sn + self._v_givenname + '\n' self._content.insert(-1, name) # ADR adr = 'ADR;TYPE=POSTAL,CHARSET=UTF-8:;' + self._v_streetaddress + self._v_l + self._v_st + self._v_postalcode + '\n' self._content.insert(-1, adr) class iopParser(ldif.LDIFParser): def __init__(self, input_file, output_file, ignored_attr_types=None,max_entries=0, process_url_schemes=None ): self.output_file = output_file ldif.LDIFParser.__init__(self,input_file,ignored_attr_types,max_entries,process_url_schemes) def handle(self, dn, entry): # We transform all the attribute type to lower case in order to make # comparisons easier. There's got to be a better way to do # case-insensitive lookup on a dictionary, but that's all I could # come up with in the short amount of time I dedicated to this. nc_entry = {} for k, v in entry.items(): nc_entry[ k.lower() ] = v del entry # Simple sanity check. if nc_entry.has_key('objectclass') is False: raise ValueError('"objectclass" is required.') # We want to trigger on the value of objectclass, being in lower case makes it # easier to compare. l = [ e.lower() for e in nc_entry['objectclass'] ] # If the entry is not of an inet Org Person, then we skip if l.count('inetorgperson') > 0: # inetOrgPerson requires cn and sn, and we really need it to build N and FN, # so we check here rather later if (nc_entry.has_key('cn') is False) or \ (nc_entry.has_key('sn') is False): raise ValueError('Attributes "cn" and "sn" are required.') else: x = vCard(nc_entry) self.output_file.writelines(x.value()) self.output_file.write('\n') else: # not a person return def main(fn_in, fn_out): import os filein = file(fn_in, 'r') # We make sure we don't overwrite an existing file. # We let exceptions rise on their own. fd = os.open(fn_out, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0666) fileout = os.fdopen(fd, 'w') vcards = iopParser(filein, fileout) vcards.parse() filein.close() fileout.close() import sys if len(sys.argv) != 3: raise ValueError('This script needs two arguments, the names of the input and output files.') else: sys.exit(main(sys.argv[1], sys.argv[2]))