1"""Provide a more Pythonic and object-oriented interface to ldb.""" 2 3# 4# Swig interface to Samba 5# 6# Copyright (C) Tim Potter 2006 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 3 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program; if not, see <http://www.gnu.org/licenses/>. 20# 21 22# 23# Interface notes: 24# 25# - should an empty dn be represented as None, or an empty string? 26# 27# - should single-valued attributes be a string, or a list with one 28# element? 29# 30 31from ldb import * 32 33# Global initialisation 34 35result = ldb_global_init() 36 37if result != 0: 38 raise LdbError, (result, 'ldb_global_init failed') 39 40# Ldb exceptions 41 42class LdbError(Exception): 43 """An exception raised when a ldb error occurs. 44 The exception data is a tuple consisting of the ldb number and a 45 string description of the error.""" 46 pass 47 48# Ldb classes 49 50class LdbMessage: 51 """A class representing a ldb message as a Python dictionary.""" 52 53 def __init__(self): 54 self.mem_ctx = talloc_init(None) 55 self.msg = ldb_msg_new(self.mem_ctx) 56 57 def __del__(self): 58 if self.mem_ctx is not None: 59 talloc_free(self.mem_ctx) 60 self.mem_ctx = None 61 self.msg = None 62 63 # Make the dn attribute of the object dynamic 64 65 def __getattr__(self, attr): 66 if attr == 'dn': 67 return ldb_dn_linearize(None, self.msg.dn) 68 return self.__dict__[attr] 69 70 def __setattr__(self, attr, value): 71 if attr == 'dn': 72 self.msg.dn = ldb_dn_explode(self.msg, value) 73 if self.msg.dn == None: 74 err = ldb.ERR_INVALID_DN_SYNTAX 75 raise LdbError(err, ldb_strerror(err)) 76 return 77 self.__dict__[attr] = value 78 79 # Get and set individual elements 80 81 def __getitem__(self, key): 82 83 elt = ldb_msg_find_element(self.msg, key) 84 85 if elt is None: 86 raise KeyError, "No such attribute '%s'" % key 87 88 return [ldb_val_array_getitem(elt.values, i) 89 for i in range(elt.num_values)] 90 91 def __setitem__(self, key, value): 92 ldb_msg_remove_attr(self.msg, key) 93 if type(value) in (list, tuple): 94 [ldb_msg_add_value(self.msg, key, v) for v in value] 95 else: 96 ldb_msg_add_value(self.msg, key, value) 97 98 # Dictionary interface 99 # TODO: move to iterator based interface 100 101 def len(self): 102 return self.msg.num_elements 103 104 def keys(self): 105 return [ldb_message_element_array_getitem(self.msg.elements, i).name 106 for i in range(self.msg.num_elements)] 107 108 def values(self): 109 return [self[k] for k in self.keys()] 110 111 def items(self): 112 return [(k, self[k]) for k in self.keys()] 113 114 # Misc stuff 115 116 def sanity_check(self): 117 return ldb_msg_sanity_check(self.msg) 118 119class Ldb: 120 """A class representing a binding to a ldb file.""" 121 122 def __init__(self, url, flags = 0): 123 """Initialise underlying ldb.""" 124 125 self.mem_ctx = talloc_init('mem_ctx for ldb 0x%x' % id(self)) 126 self.ldb_ctx = ldb_init(self.mem_ctx) 127 128 result = ldb_connect(self.ldb_ctx, url, flags, None) 129 130 if result != LDB_SUCCESS: 131 raise LdbError, (result, ldb_strerror(result)) 132 133 def __del__(self): 134 """Called when the object is to be garbage collected.""" 135 self.close() 136 137 def close(self): 138 """Close down a ldb.""" 139 if self.mem_ctx is not None: 140 talloc_free(self.mem_ctx) 141 self.mem_ctx = None 142 self.ldb_ctx = None 143 144 def _ldb_call(self, fn, *args): 145 """Call a ldb function with args. Raise a LdbError exception 146 if the function returns a non-zero return value.""" 147 148 result = fn(*args) 149 150 if result != LDB_SUCCESS: 151 raise LdbError, (result, ldb_strerror(result)) 152 153 def search(self, expression): 154 """Search a ldb for a given expression.""" 155 156 self._ldb_call(ldb_search, self.ldb_ctx, None, LDB_SCOPE_DEFAULT, 157 expression, None); 158 159 return [LdbMessage(ldb_message_ptr_array_getitem(result.msgs, ndx)) 160 for ndx in range(result.count)] 161 162 def delete(self, dn): 163 """Delete a dn.""" 164 165 _dn = ldb_dn_explode(self.ldb_ctx, dn) 166 167 self._ldb_call(ldb_delete, self.ldb_ctx, _dn) 168 169 def rename(self, olddn, newdn): 170 """Rename a dn.""" 171 172 _olddn = ldb_dn_explode(self.ldb_ctx, olddn) 173 _newdn = ldb_dn_explode(self.ldb_ctx, newdn) 174 175 self._ldb_call(ldb_rename, self.ldb_ctx, _olddn, _newdn) 176 177 def add(self, m): 178 self._ldb_call(ldb_add, self.ldb_ctx, m.msg) 179