a6532b7f0d619ec65d77a693295a65faefe70a47
[kai/samba-autobuild/.git] / source4 / scripting / python / samba / __init__.py
1 #!/usr/bin/python
2
3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
5 #
6 # Based on the original in EJS:
7 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 """Samba 4."""
24
25 __docformat__ = "restructuredText"
26
27 import os
28 import sys
29
30 def _in_source_tree():
31     """Check whether the script is being run from the source dir. """
32     return os.path.exists("%s/../../../selftest/skip" % os.path.dirname(__file__))
33
34
35 # When running, in-tree, make sure bin/python is in the PYTHONPATH
36 if _in_source_tree():
37     srcdir = "%s/../../.." % os.path.dirname(__file__)
38     sys.path.append("%s/bin/python" % srcdir)
39     default_ldb_modules_dir = "%s/bin/modules/ldb" % srcdir
40 else:
41     default_ldb_modules_dir = None
42
43
44 import ldb
45 import _glue
46 from samba._ldb import Ldb as _Ldb
47
48 class Ldb(_Ldb):
49     """Simple Samba-specific LDB subclass that takes care
50     of setting up the modules dir, credentials pointers, etc.
51
52     Please note that this is intended to be for all Samba LDB files,
53     not necessarily the Sam database. For Sam-specific helper
54     functions see samdb.py.
55     """
56     def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
57                  credentials=None, flags=0, options=None):
58         """Opens a Samba Ldb file.
59
60         :param url: Optional LDB URL to open
61         :param lp: Optional loadparm object
62         :param modules_dir: Optional modules directory
63         :param session_info: Optional session information
64         :param credentials: Optional credentials, defaults to anonymous.
65         :param flags: Optional LDB flags
66         :param options: Additional options (optional)
67
68         This is different from a regular Ldb file in that the Samba-specific
69         modules-dir is used by default and that credentials and session_info
70         can be passed through (required by some modules).
71         """
72
73         if modules_dir is not None:
74             self.set_modules_dir(modules_dir)
75         elif default_ldb_modules_dir is not None:
76             self.set_modules_dir(default_ldb_modules_dir)
77         elif lp is not None:
78             self.set_modules_dir(os.path.join(lp.get("modules dir"), "ldb"))
79
80         if session_info is not None:
81             self.set_session_info(session_info)
82
83         if credentials is not None:
84             self.set_credentials(credentials)
85
86         if lp is not None:
87             self.set_loadparm(lp)
88
89         # This must be done before we load the schema, as these handlers for
90         # objectSid and objectGUID etc must take precedence over the 'binary
91         # attribute' declaration in the schema
92         self.register_samba_handlers()
93
94         # TODO set debug
95         def msg(l,text):
96             print text
97         #self.set_debug(msg)
98
99         self.set_utf8_casefold()
100
101         # Allow admins to force non-sync ldb for all databases
102         if lp is not None:
103             nosync_p = lp.get("nosync", "ldb")
104             if nosync_p is not None and nosync_p == True:
105                 flags |= ldb.FLG_NOSYNC
106
107         self.set_create_perms(0600)
108
109         if url is not None:
110             self.connect(url, flags, options)
111
112     def searchone(self, attribute, basedn=None, expression=None,
113                   scope=ldb.SCOPE_BASE):
114         """Search for one attribute as a string.
115
116         :param basedn: BaseDN for the search.
117         :param attribute: Name of the attribute
118         :param expression: Optional search expression.
119         :param scope: Search scope (defaults to base).
120         :return: Value of attribute as a string or None if it wasn't found.
121         """
122         res = self.search(basedn, scope, expression, [attribute])
123         if len(res) != 1 or res[0][attribute] is None:
124             return None
125         values = set(res[0][attribute])
126         assert len(values) == 1
127         return self.schema_format_value(attribute, values.pop())
128
129     def erase_users_computers(self, dn):
130         """Erases user and computer objects from our AD.
131         
132         This is needed since the 'samldb' module denies the deletion of primary
133         groups. Therefore all groups shouldn't be primary somewhere anymore.
134         """
135
136         try:
137             res = self.search(base=dn, scope=ldb.SCOPE_SUBTREE, attrs=[],
138                       expression="(|(objectclass=user)(objectclass=computer))")
139         except ldb.LdbError, (errno, _):
140             if errno == ldb.ERR_NO_SUCH_OBJECT:
141                 # Ignore no such object errors
142                 return
143             else:
144                 raise
145
146         try:
147             for msg in res:
148                 self.delete(msg.dn, ["relax:0"])
149         except ldb.LdbError, (errno, _):
150             if errno != ldb.ERR_NO_SUCH_OBJECT:
151                 # Ignore no such object errors
152                 raise
153
154     def erase_except_schema_controlled(self):
155         """Erase this ldb.
156         
157         :note: Removes all records, except those that are controlled by
158             Samba4's schema.
159         """
160
161         basedn = ""
162
163         # Try to delete user/computer accounts to allow deletion of groups
164         self.erase_users_computers(basedn)
165
166         # Delete the 'visible' records, and the invisble 'deleted' records (if this DB supports it)
167         for msg in self.search(basedn, ldb.SCOPE_SUBTREE,
168                        "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))",
169                        [], controls=["show_deleted:0"]):
170             try:
171                 self.delete(msg.dn, ["relax:0"])
172             except ldb.LdbError, (errno, _):
173                 if errno != ldb.ERR_NO_SUCH_OBJECT:
174                     # Ignore no such object errors
175                     raise
176
177         res = self.search(basedn, ldb.SCOPE_SUBTREE,
178             "(&(|(objectclass=*)(distinguishedName=*))(!(distinguishedName=@BASEINFO)))", [], controls=["show_deleted:0"])
179         assert len(res) == 0
180
181         # delete the specials
182         for attr in ["@SUBCLASSES", "@MODULES",
183                      "@OPTIONS", "@PARTITION", "@KLUDGEACL"]:
184             try:
185                 self.delete(attr, ["relax:0"])
186             except ldb.LdbError, (errno, _):
187                 if errno != ldb.ERR_NO_SUCH_OBJECT:
188                     # Ignore missing dn errors
189                     raise
190
191     def erase(self):
192         """Erase this ldb, removing all records."""
193         self.erase_except_schema_controlled()
194
195         # delete the specials
196         for attr in ["@INDEXLIST", "@ATTRIBUTES"]:
197             try:
198                 self.delete(attr, ["relax:0"])
199             except ldb.LdbError, (errno, _):
200                 if errno != ldb.ERR_NO_SUCH_OBJECT:
201                     # Ignore missing dn errors
202                     raise
203
204     def erase_partitions(self):
205         """Erase an ldb, removing all records."""
206
207         def erase_recursive(self, dn):
208             try:
209                 res = self.search(base=dn, scope=ldb.SCOPE_ONELEVEL, attrs=[],
210                                   controls=["show_deleted:0"])
211             except ldb.LdbError, (errno, _):
212                 if errno == ldb.ERR_NO_SUCH_OBJECT:
213                     # Ignore no such object errors
214                     return
215
216             for msg in res:
217                 erase_recursive(self, msg.dn)
218
219             try:
220                 self.delete(dn, ["relax:0"])
221             except ldb.LdbError, (errno, _):
222                 if errno != ldb.ERR_NO_SUCH_OBJECT:
223                     # Ignore no such object errors
224                     raise
225
226         res = self.search("", ldb.SCOPE_BASE, "(objectClass=*)",
227                          ["namingContexts"])
228         assert len(res) == 1
229         if not "namingContexts" in res[0]:
230             return
231         for basedn in res[0]["namingContexts"]:
232             # Try to delete user/computer accounts to allow deletion of groups
233             self.erase_users_computers(basedn)
234             # Try and erase from the bottom-up in the tree
235             erase_recursive(self, basedn)
236
237     def load_ldif_file_add(self, ldif_path):
238         """Load a LDIF file.
239
240         :param ldif_path: Path to LDIF file.
241         """
242         self.add_ldif(open(ldif_path, 'r').read())
243
244     def add_ldif(self, ldif, controls=None):
245         """Add data based on a LDIF string.
246
247         :param ldif: LDIF text.
248         """
249         for changetype, msg in self.parse_ldif(ldif):
250             assert changetype == ldb.CHANGETYPE_NONE
251             self.add(msg,controls)
252
253     def modify_ldif(self, ldif, controls=None):
254         """Modify database based on a LDIF string.
255
256         :param ldif: LDIF text.
257         """
258         for changetype, msg in self.parse_ldif(ldif):
259             if changetype == ldb.CHANGETYPE_ADD:
260                 self.add(msg, controls)
261             else:
262                 self.modify(msg, controls)
263
264
265 def substitute_var(text, values):
266     """Substitute strings of the form ${NAME} in str, replacing
267     with substitutions from subobj.
268
269     :param text: Text in which to subsitute.
270     :param values: Dictionary with keys and values.
271     """
272
273     for (name, value) in values.items():
274         assert isinstance(name, str), "%r is not a string" % name
275         assert isinstance(value, str), "Value %r for %s is not a string" % (value, name)
276         text = text.replace("${%s}" % name, value)
277
278     return text
279
280
281 def check_all_substituted(text):
282     """Make sure that all substitution variables in a string have been replaced.
283     If not, raise an exception.
284
285     :param text: The text to search for substitution variables
286     """
287     if not "${" in text:
288         return
289
290     var_start = text.find("${")
291     var_end = text.find("}", var_start)
292
293     raise Exception("Not all variables substituted: %s" % text[var_start:var_end+1])
294
295
296 def read_and_sub_file(file, subst_vars):
297     """Read a file and sub in variables found in it
298
299     :param file: File to be read (typically from setup directory)
300      param subst_vars: Optional variables to subsitute in the file.
301     """
302     data = open(file, 'r').read()
303     if subst_vars is not None:
304         data = substitute_var(data, subst_vars)
305         check_all_substituted(data)
306     return data
307
308
309 def setup_file(template, fname, subst_vars=None):
310     """Setup a file in the private dir.
311
312     :param template: Path of the template file.
313     :param fname: Path of the file to create.
314     :param subst_vars: Substitution variables.
315     """
316     if os.path.exists(fname):
317         os.unlink(fname)
318
319     data = read_and_sub_file(template, subst_vars)
320     f = open(fname, 'w')
321     try:
322         f.write(data)
323     finally:
324         f.close()
325
326
327 def valid_netbios_name(name):
328     """Check whether a name is valid as a NetBIOS name. """
329     # See crh's book (1.4.1.1)
330     if len(name) > 15:
331         return False
332     for x in name:
333         if not x.isalnum() and not x in " !#$%&'()-.@^_{}~":
334             return False
335     return True
336
337
338 def ensure_external_module(modulename, location):
339     """Add a location to sys.path if an external dependency can't be found.
340
341     :param modulename: Module name to import
342     :param location: Location to add to sys.path (can be relative to 
343         ${srcdir}/lib
344     """
345     try:
346         __import__(modulename)
347     except ImportError:
348         import sys
349         if _in_source_tree():
350             sys.path.insert(0, 
351                 os.path.join(os.path.dirname(__file__),
352                              "../../../../lib", location))
353             __import__(modulename)
354         else:
355             sys.modules[modulename] = __import__(
356                 "samba.external.%s" % modulename, fromlist=["samba.external"])
357
358 version = _glue.version
359 interface_ips = _glue.interface_ips
360 set_debug_level = _glue.set_debug_level
361 unix2nttime = _glue.unix2nttime
362 generate_random_password = _glue.generate_random_password