""" ldif - generate and parse LDIF data (see RFC 2849) See http://www.python-ldap.org/ for details. $Id: ldif.py,v 1.87 2015/10/24 16:12:31 stroeder Exp $ Python compability note: Tested with Python 2.0+, but should work with Python 1.5.2+. """ __version__ = '2.4.22' __all__ = [ # constants 'ldif_pattern', # functions 'CreateLDIF','ParseLDIF', # classes 'LDIFWriter', 'LDIFParser', 'LDIFRecordList', 'LDIFCopy', ] import urllib import base64 import re import types import sys try: from cStringIO import StringIO except ImportError: try: from StringIO import StringIO except ImportError: from io import StringIO from ldap.compat import urlparse attrtype_pattern = r'[\w;.-]+(;[\w_-]+)*' attrvalue_pattern = r'(([^,]|\\,)+|".*?")' attrtypeandvalue_pattern = attrtype_pattern + r'[ ]*=[ ]*' + attrvalue_pattern rdn_pattern = attrtypeandvalue_pattern + r'([ ]*\+[ ]*' + attrtypeandvalue_pattern + r')*[ ]*' dn_pattern = rdn_pattern + r'([ ]*,[ ]*' + rdn_pattern + r')*[ ]*' dn_regex = re.compile('^%s$' % dn_pattern) ldif_pattern = '^((dn(:|::) %(dn_pattern)s)|(%(attrtype_pattern)s(:|::) .*)$)+' % vars() MOD_OP_INTEGER = { 'add' :0, # ldap.MOD_REPLACE 'delete' :1, # ldap.MOD_DELETE 'replace':2, # ldap.MOD_REPLACE } MOD_OP_STR = { 0:'add',1:'delete',2:'replace' } CHANGE_TYPES = ['add','delete','modify','modrdn'] valid_changetype_dict = {} for c in CHANGE_TYPES: valid_changetype_dict[c]=None def is_dn(s): """ returns 1 if s is a LDAP DN """ if s=='': return 1 rm = dn_regex.match(s) return rm!=None and rm.group(0)==s SAFE_STRING_PATTERN = b'(^(\000|\n|\r| |:|<)|[\000\n\r\200-\377]+|[ ]+$)' safe_string_re = re.compile(SAFE_STRING_PATTERN) def list_dict(l): """ return a dictionary with all items of l being the keys of the dictionary """ return dict([(i,None) for i in l]) class LDIFWriter: """ Write LDIF entry or change records to file object Copy LDIF input to a file output object containing all data retrieved via URLs """ def __init__(self,output_file,base64_attrs=None,cols=76,line_sep='\n'): """ output_file file object for output base64_attrs list of attribute types to be base64-encoded in any case cols Specifies how many columns a line may have before it's folded into many lines. line_sep String used as line separator """ self._output_file = output_file self._base64_attrs = list_dict([a.lower() for a in (base64_attrs or [])]) self._cols = cols self._line_sep = line_sep self.records_written = 0 def _unfold_lines(self,line): """ Write string line as one or more folded lines """ # Check maximum line length line_len = len(line) if line_len<=self._cols: self._output_file.write(line) self._output_file.write(self._line_sep) else: # Fold line pos = self._cols self._output_file.write(line[0:min(line_len,self._cols)]) self._output_file.write(self._line_sep) while pos