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 |
|
---|
31 | from ldb import *
|
---|
32 |
|
---|
33 | # Global initialisation
|
---|
34 |
|
---|
35 | result = ldb_global_init()
|
---|
36 |
|
---|
37 | if result != 0:
|
---|
38 | raise LdbError, (result, 'ldb_global_init failed')
|
---|
39 |
|
---|
40 | # Ldb exceptions
|
---|
41 |
|
---|
42 | class 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 |
|
---|
50 | class 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 |
|
---|
119 | class 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)
|
---|