Package ldaptor :: Package protocols :: Package ldap :: Module ldifprotocol
[hide private]
[frames] | no frames]

Source Code for Module ldaptor.protocols.ldap.ldifprotocol

  1  import base64 
  2   
  3  from twisted.protocols import basic 
  4  from twisted.internet import protocol 
  5   
  6  from ldaptor import entry 
  7   
8 -class LDIFParseError(Exception):
9 """Error parsing LDIF.""" 10
11 - def __str__(self):
12 s = self.__doc__ 13 if self[:]: 14 s = ': '.join([s]+map(str, self[:])) 15 return s+'.'
16
17 -class LDIFLineWithoutSemicolonError(LDIFParseError):
18 """LDIF line without semicolon seen""" 19 pass
20
21 -class LDIFEntryStartsWithNonDNError(LDIFParseError):
22 """LDIF entry starts with a non-DN line""" 23 pass
24
25 -class LDIFEntryStartsWithSpaceError(LDIFParseError):
26 """Invalid LDIF value format""" 27 pass
28
29 -class LDIFVersionNotANumberError(LDIFParseError):
30 """Non-numeric LDIF version number""" 31 pass
32
33 -class LDIFUnsupportedVersionError(LDIFParseError):
34 """LDIF version not supported""" 35 pass
36
37 -class LDIFTruncatedError(LDIFParseError):
38 """LDIF appears to be truncated""" 39 pass
40 41 HEADER = 'HEADER' 42 WAIT_FOR_DN = 'WAIT_FOR_DN' 43 IN_ENTRY = 'IN_ENTRY' 44
45 -class LDIF(object, basic.LineReceiver):
46 delimiter='\n' 47 mode = HEADER 48 49 dn = None 50 data = None 51 lastLine = None 52 53 version = None 54
55 - def logicalLineReceived(self, line):
56 if line.startswith('#'): 57 # comments are allowed everywhere 58 return 59 getattr(self, 'state_' + self.mode)(line)
60
61 - def lineReceived(self, line):
62 if line.startswith(' '): 63 if self.lastLine is None: 64 raise LDIFEntryStartsWithSpaceError 65 self.lastLine = self.lastLine + line[1:] 66 else: 67 if self.lastLine is not None: 68 self.logicalLineReceived(self.lastLine) 69 self.lastLine = line 70 if line == '': 71 self.logicalLineReceived(line) 72 self.lastLine = None
73
74 - def parseValue(self, val):
75 if val.startswith(':'): 76 return base64.decodestring(val[1:].lstrip(' ')) 77 elif val.startswith('<'): 78 raise NotImplementedError 79 else: 80 return val.lstrip(' ')
81
82 - def _parseLine(self, line):
83 try: 84 key, val = line.split(':', 1) 85 except ValueError: 86 # unpack list of wrong size 87 # -> invalid input data 88 raise LDIFLineWithoutSemicolonError, line 89 val = self.parseValue(val) 90 return key, val
91
92 - def state_HEADER(self, line):
93 key, val = self._parseLine(line) 94 self.mode = WAIT_FOR_DN 95 96 if key != 'version': 97 self.logicalLineReceived(line) 98 else: 99 try: 100 version = int(val) 101 except ValueError: 102 raise LDIFVersionNotANumberError, val 103 self.version = version 104 if version > 1: 105 raise LDIFUnsupportedVersionError, version
106
107 - def state_WAIT_FOR_DN(self, line):
108 assert self.dn is None, 'self.dn must not be set when waiting for DN' 109 assert self.data is None, 'self.data must not be set when waiting for DN' 110 if line == '': 111 # too many empty lines, but be tolerant 112 return 113 114 key, val = self._parseLine(line) 115 116 if key.upper() != 'DN': 117 raise LDIFEntryStartsWithNonDNError, line 118 119 self.dn = val 120 self.data = {} 121 self.mode = IN_ENTRY
122
123 - def state_IN_ENTRY(self, line):
124 assert self.dn is not None, 'self.dn must be set when in entry' 125 assert self.data is not None, 'self.data must be set when in entry' 126 127 if line == '': 128 # end of entry 129 self.mode = WAIT_FOR_DN 130 o = entry.BaseLDAPEntry(dn=self.dn, 131 attributes=self.data) 132 self.dn = None 133 self.data = None 134 self.gotEntry(o) 135 return 136 137 key, val = self._parseLine(line) 138 139 if not key in self.data: 140 self.data[key] = [] 141 142 self.data[key].append(val)
143
144 - def gotEntry(self, obj):
145 pass
146
147 - def connectionLost(self, reason=protocol.connectionDone):
148 if self.mode != WAIT_FOR_DN: 149 raise LDIFTruncatedError, reason
150