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