summaryrefslogtreecommitdiffstats
path: root/lib/smbconf
diff options
context:
space:
mode:
Diffstat (limited to 'lib/smbconf')
-rw-r--r--lib/smbconf/pysmbconf.c813
-rw-r--r--lib/smbconf/pysmbconf.h36
-rw-r--r--lib/smbconf/smbconf.c511
-rw-r--r--lib/smbconf/smbconf.h505
-rw-r--r--lib/smbconf/smbconf_private.h96
-rw-r--r--lib/smbconf/smbconf_txt.c682
-rw-r--r--lib/smbconf/smbconf_txt.h33
-rw-r--r--lib/smbconf/smbconf_util.c151
-rw-r--r--lib/smbconf/wscript_build10
9 files changed, 2837 insertions, 0 deletions
diff --git a/lib/smbconf/pysmbconf.c b/lib/smbconf/pysmbconf.c
new file mode 100644
index 0000000..2538127
--- /dev/null
+++ b/lib/smbconf/pysmbconf.c
@@ -0,0 +1,813 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library - Python bindings
+ *
+ * Copyright (C) John Mulligan <phlogistonjohn@asynchrono.us> 2022
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "lib/replace/system/python.h"
+#include "includes.h"
+#include "python/py3compat.h"
+
+#include "lib/smbconf/smbconf.h"
+#include "lib/smbconf/smbconf_txt.h"
+#include "lib/smbconf/pysmbconf.h"
+
+static PyObject *PyExc_SMBConfError;
+
+static void py_raise_SMBConfError(sbcErr err)
+{
+ PyObject *v = NULL;
+ PyObject *args = NULL;
+
+ /*
+ * TODO: have the exception type accept arguments in new/init
+ */
+ args = Py_BuildValue("(is)", err, sbcErrorString(err));
+ if (args == NULL) {
+ PyErr_Format(PyExc_SMBConfError, "[%d]: %s", err,
+ sbcErrorString(err));
+ return;
+ }
+ v = PyObject_Call(PyExc_SMBConfError, args, NULL);
+ if (v == NULL) {
+ Py_CLEAR(args);
+ return;
+ }
+ /*
+ * It's clearer to set an explicit error_code attribute for use in calling
+ * code to check what kind of SMBConfError was raised.
+ */
+ if (PyObject_SetAttrString(v, "error_code", PyTuple_GetItem(args, 0)) == -1) {
+ Py_CLEAR(v);
+ Py_CLEAR(args);
+ return;
+ }
+ Py_CLEAR(args);
+ PyErr_SetObject((PyObject *) Py_TYPE(v), v);
+ Py_DECREF(v);
+}
+
+/*
+ * py_from_smbconf_service returns a python tuple that is basically equivalent
+ * to the struct smbconf_service type content-wise.
+ */
+static PyObject *py_from_smbconf_service(struct smbconf_service *svc)
+{
+ uint32_t count;
+ PyObject *plist = PyList_New(svc->num_params);
+ if (plist == NULL) {
+ return NULL;
+ }
+
+ for (count = 0; count < svc->num_params; count++) {
+ PyObject *pt = Py_BuildValue("(ss)",
+ svc->param_names[count],
+ svc->param_values[count]);
+ if (pt == NULL) {
+ Py_CLEAR(plist);
+ return NULL;
+ }
+ if (PyList_SetItem(plist, count, pt) < 0) {
+ Py_CLEAR(pt);
+ Py_CLEAR(plist);
+ return NULL;
+ }
+ }
+ return Py_BuildValue("(sO)", svc->name, plist);
+}
+
+static PyObject *obj_new(PyTypeObject * type, PyObject * args, PyObject * kwds)
+{
+ py_SMBConf_Object *self = (py_SMBConf_Object *) type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->mem_ctx = talloc_new(NULL);
+ if (self->mem_ctx == NULL) {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ return (PyObject *) self;
+}
+
+static void obj_dealloc(py_SMBConf_Object * self)
+{
+ if (self->conf_ctx != NULL) {
+ smbconf_shutdown(self->conf_ctx);
+ }
+ talloc_free(self->mem_ctx);
+ Py_TYPE(self)->tp_free((PyObject *) self);
+}
+
+static bool obj_ready(py_SMBConf_Object * self)
+{
+ if (self->conf_ctx == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "attempt to use an uninitialized SMBConf object");
+ return false;
+ }
+ return true;
+}
+
+static PyObject *obj_requires_messaging(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ if (!obj_ready(self)) {
+ return NULL;
+ }
+ if (smbconf_backend_requires_messaging(self->conf_ctx)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static PyObject *obj_is_writable(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ if (!obj_ready(self)) {
+ return NULL;
+ }
+ if (smbconf_is_writeable(self->conf_ctx)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static PyObject *obj_share_names(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ sbcErr err;
+ uint32_t count;
+ uint32_t num_shares;
+ char **share_names = NULL;
+ PyObject *slist = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ if (!obj_ready(self)) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ err =
+ smbconf_get_share_names(self->conf_ctx, mem_ctx, &num_shares,
+ &share_names);
+ if (err != SBC_ERR_OK) {
+ talloc_free(mem_ctx);
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+
+ slist = PyList_New(num_shares);
+ if (slist == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ for (count = 0; count < num_shares; count++) {
+ PyObject *ustr = PyUnicode_FromString(share_names[count]);
+ if (ustr == NULL) {
+ Py_CLEAR(slist);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ if (PyList_SetItem(slist, count, ustr) < 0) {
+ Py_CLEAR(ustr);
+ Py_CLEAR(slist);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+ talloc_free(mem_ctx);
+ return slist;
+}
+
+static PyObject *obj_get_share(py_SMBConf_Object * self, PyObject * args)
+{
+ sbcErr err;
+ char *servicename = NULL;
+ struct smbconf_service *svc = NULL;
+ PyObject *plist = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &servicename)) {
+ return NULL;
+ }
+
+ if (!obj_ready(self)) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ err = smbconf_get_share(self->conf_ctx, mem_ctx, servicename, &svc);
+ if (err != SBC_ERR_OK) {
+ talloc_free(mem_ctx);
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ /*
+ * if py_from_smbconf_service returns NULL, then an exception should
+ * already be set. No special error handling needed.
+ */
+ plist = py_from_smbconf_service(svc);
+ talloc_free(mem_ctx);
+ return plist;
+}
+
+static PyObject *obj_get_config(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ sbcErr err;
+ PyObject *svclist = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ uint32_t count;
+ uint32_t num_shares;
+ struct smbconf_service **svcs = NULL;
+
+ if (!obj_ready(self)) {
+ return NULL;
+ }
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ err = smbconf_get_config(self->conf_ctx, mem_ctx, &num_shares, &svcs);
+ if (err != SBC_ERR_OK) {
+ talloc_free(mem_ctx);
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+
+ svclist = PyList_New(num_shares);
+ if (svclist == NULL) {
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ for (count = 0; count < num_shares; count++) {
+ PyObject *svcobj = py_from_smbconf_service(svcs[count]);
+ if (svcobj == NULL) {
+ Py_CLEAR(svclist);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ if (PyList_SetItem(svclist, count, svcobj) < 0) {
+ Py_CLEAR(svcobj);
+ Py_CLEAR(svclist);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return svclist;
+}
+
+static PyObject *obj_create_share(py_SMBConf_Object * self, PyObject * args)
+{
+ sbcErr err;
+ char *servicename = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &servicename)) {
+ return NULL;
+ }
+
+ err = smbconf_create_share(self->conf_ctx, servicename);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_drop(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ sbcErr err;
+
+ err = smbconf_drop(self->conf_ctx);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_set_parameter(py_SMBConf_Object * self, PyObject * args)
+{
+ sbcErr err;
+ char *servicename = NULL;
+ char *param = NULL;
+ char *val = NULL;
+
+ if (!PyArg_ParseTuple(args, "sss", &servicename, &param, &val)) {
+ return NULL;
+ }
+
+ err = smbconf_set_parameter(self->conf_ctx, servicename, param, val);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_set_global_parameter(py_SMBConf_Object * self,
+ PyObject * args)
+{
+ sbcErr err;
+ char *param = NULL;
+ char *val = NULL;
+
+ if (!PyArg_ParseTuple(args, "ss", &param, &val)) {
+ return NULL;
+ }
+
+ err = smbconf_set_global_parameter(self->conf_ctx, param, val);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_delete_share(py_SMBConf_Object * self, PyObject * args)
+{
+ sbcErr err;
+ char *servicename = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &servicename)) {
+ return NULL;
+ }
+
+ err = smbconf_delete_share(self->conf_ctx, servicename);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static char *py_get_kv_str(TALLOC_CTX * mem_ctx, PyObject * obj, Py_ssize_t idx)
+{
+ char *ss = NULL;
+ PyObject *pystr = PySequence_GetItem(obj, idx);
+ if (pystr == NULL) {
+ return NULL;
+ }
+ if (!PyUnicode_Check(pystr)) {
+ PyErr_SetString(PyExc_TypeError, "keys/values expect a str");
+ Py_CLEAR(pystr);
+ return NULL;
+ }
+ ss = talloc_strdup(mem_ctx, PyUnicode_AsUTF8(pystr));
+ Py_CLEAR(pystr);
+ return ss;
+}
+
+static PyObject *obj_create_set_share(py_SMBConf_Object * self, PyObject * args)
+{
+ sbcErr err;
+ char *servicename = NULL;
+ PyObject *kvs = NULL;
+ Py_ssize_t size, idx;
+ struct smbconf_service *tmp_service = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(self->mem_ctx);
+
+ if (!PyArg_ParseTuple(args, "sO", &servicename, &kvs)) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ if (PySequence_Check(kvs) == 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "a sequence object is required");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ size = PySequence_Size(kvs);
+ if (size == -1) {
+ PyErr_SetString(PyExc_ValueError, "failed to get size");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ tmp_service = talloc_zero(tmp_ctx, struct smbconf_service);
+ if (tmp_service == NULL) {
+ PyErr_NoMemory();
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ tmp_service->name = talloc_strdup(tmp_service, servicename);
+ if (tmp_service->name == NULL) {
+ PyErr_NoMemory();
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ tmp_service->num_params = (uint32_t) size;
+ tmp_service->param_names = talloc_array(tmp_ctx, char *, size);
+ if (tmp_service->param_names == NULL) {
+ PyErr_NoMemory();
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ tmp_service->param_values = talloc_array(tmp_ctx, char *, size);
+ if (tmp_service->param_values == NULL) {
+ PyErr_NoMemory();
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ for (idx = 0; idx < size; idx++) {
+ char *tmp_str = NULL;
+ PyObject *tmp_pair = PySequence_GetItem(kvs, idx);
+ if (tmp_pair == NULL) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ if (PySequence_Size(tmp_pair) != 2) {
+ PyErr_SetString(PyExc_ValueError,
+ "expecting two-item tuples");
+ Py_CLEAR(tmp_pair);
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ /* fetch key */
+ tmp_str = py_get_kv_str(tmp_ctx, tmp_pair, 0);
+ if (tmp_str == NULL) {
+ Py_CLEAR(tmp_pair);
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ tmp_service->param_names[idx] = tmp_str;
+
+ /* fetch value */
+ tmp_str = py_get_kv_str(tmp_ctx, tmp_pair, 1);
+ if (tmp_str == NULL) {
+ Py_CLEAR(tmp_pair);
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ tmp_service->param_values[idx] = tmp_str;
+
+ Py_CLEAR(tmp_pair);
+ }
+
+ err = smbconf_create_set_share(self->conf_ctx, tmp_service);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ talloc_free(tmp_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_delete_parameter(py_SMBConf_Object * self, PyObject * args)
+{
+ sbcErr err;
+ char *servicename = NULL;
+ char *param_name = NULL;
+
+ if (!PyArg_ParseTuple(args, "ss", &servicename, &param_name)) {
+ return NULL;
+ }
+
+ err = smbconf_delete_parameter(self->conf_ctx, servicename, param_name);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_delete_global_parameter(py_SMBConf_Object * self,
+ PyObject * args)
+{
+ sbcErr err;
+ char *param_name = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &param_name)) {
+ return NULL;
+ }
+
+ err = smbconf_delete_global_parameter(self->conf_ctx, param_name);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_transaction_start(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ sbcErr err = smbconf_transaction_start(self->conf_ctx);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_transaction_commit(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ sbcErr err = smbconf_transaction_commit(self->conf_ctx);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *obj_transaction_cancel(py_SMBConf_Object * self,
+ PyObject * Py_UNUSED(ignored))
+{
+ sbcErr err = smbconf_transaction_cancel(self->conf_ctx);
+ if (err != SBC_ERR_OK) {
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(obj_requires_messaging_doc,
+"requires_messaging() -> bool\n"
+"\n"
+"Returns true if the backend requires interprocess messaging.\n");
+
+PyDoc_STRVAR(obj_is_writable_doc,
+"is_writeable() -> bool\n"
+"\n"
+"Returns true if the SMBConf object's backend is writable.\n");
+
+PyDoc_STRVAR(obj_share_names_doc,
+"share_names() -> list[str]\n"
+"\n"
+"Return a list of the share names currently configured.\n"
+"Includes the global section as a share name.\n");
+
+PyDoc_STRVAR(obj_get_share_doc,
+"get_share() -> (str, list[(str, str)])\n"
+"\n"
+"Given the name of a share, return a tuple of \n"
+"(share_name, share_parms) where share_params is a list of\n"
+"(param_name, param_value) tuples.\n"
+"The term global can be specified to get global section parameters.\n");
+
+PyDoc_STRVAR(obj_get_config_doc,
+"get_config() -> list[(str, list[(str, str)])]\n"
+"Return a list of tuples for every section/share of the current\n"
+"configuration. Each tuple in the list is the same as described\n"
+"for get_share().\n");
+
+PyDoc_STRVAR(obj_create_share_doc,
+"create_share(name: str) -> None\n"
+"Create a new empty share in the configuration. The share\n"
+"name must not exist or an error will be raised.\n");
+
+PyDoc_STRVAR(obj_drop_doc,
+"drop() -> None\n"
+"Drop the entire configuration, resetting it to an empty state.\n");
+
+PyDoc_STRVAR(obj_set_parameter_doc,
+"set_parameter(str, str, str) -> None\n"
+"Set a configuration parameter. Specify service name, parameter name,\n"
+"and parameter value.\n");
+
+PyDoc_STRVAR(obj_set_global_parameter_doc,
+"set_global_parameter(str, str) -> None\n"
+"Set a global configuration parameter. Specify the parameter name\n"
+"and parameter value.\n");
+
+PyDoc_STRVAR(obj_delete_share_doc,
+"delete_share(str) -> None\n"
+"Delete a service from the configuration.\n");
+
+PyDoc_STRVAR(obj_create_set_share_doc,
+"create_set_share(str, [(str, str)...]) -> None\n"
+"Create and set the definition of a service.\n");
+
+PyDoc_STRVAR(obj_delete_parameter_doc,
+"delete_parameter(str, str) -> None\n"
+"Delete a single configuration parameter.\n");
+
+PyDoc_STRVAR(obj_delete_global_parameter_doc,
+"delete_parameter(str, str) -> None\n"
+"Delete a single global configuration parameter.\n");
+
+PyDoc_STRVAR(obj_transaction_start_doc,
+"transaction_start() -> None\n"
+"Start a transaction.\n"
+"Transactions allow making compound sets of changes atomically.\n");
+
+PyDoc_STRVAR(obj_transaction_commit_doc,
+"transaction_commit() -> None\n"
+"Commit the transaction.\n");
+
+PyDoc_STRVAR(obj_transaction_cancel_doc,
+"transaction_cancel() -> None\n"
+"Cancel the transaction.\n");
+
+static PyMethodDef py_smbconf_obj_methods[] = {
+ { "requires_messaging", (PyCFunction) obj_requires_messaging,
+ METH_NOARGS, obj_requires_messaging_doc },
+ { "is_writeable", (PyCFunction) obj_is_writable, METH_NOARGS,
+ obj_is_writable_doc },
+ { "share_names", (PyCFunction) obj_share_names, METH_NOARGS,
+ obj_share_names_doc },
+ { "get_share", (PyCFunction) obj_get_share, METH_VARARGS,
+ obj_get_share_doc },
+ { "get_config", (PyCFunction) obj_get_config, METH_NOARGS,
+ obj_get_config_doc },
+ { "create_share", (PyCFunction) obj_create_share, METH_VARARGS,
+ obj_create_share_doc },
+ { "create_set_share", (PyCFunction) obj_create_set_share, METH_VARARGS,
+ obj_create_set_share_doc },
+ { "drop", (PyCFunction) obj_drop, METH_NOARGS,
+ obj_drop_doc },
+ { "set_parameter", (PyCFunction) obj_set_parameter, METH_VARARGS,
+ obj_set_parameter_doc },
+ { "set_global_parameter", (PyCFunction) obj_set_global_parameter,
+ METH_VARARGS, obj_set_global_parameter_doc },
+ { "delete_share", (PyCFunction) obj_delete_share, METH_VARARGS,
+ obj_delete_share_doc },
+ { "delete_parameter", (PyCFunction) obj_delete_parameter, METH_VARARGS,
+ obj_delete_parameter_doc },
+ { "delete_global_parameter", (PyCFunction) obj_delete_global_parameter,
+ METH_VARARGS, obj_delete_global_parameter_doc },
+ { "transaction_start", (PyCFunction) obj_transaction_start, METH_NOARGS,
+ obj_transaction_start_doc },
+ { "transaction_commit", (PyCFunction) obj_transaction_commit,
+ METH_NOARGS, obj_transaction_commit_doc },
+ { "transaction_cancel", (PyCFunction) obj_transaction_cancel,
+ METH_NOARGS, obj_transaction_cancel_doc },
+ { 0 },
+};
+
+PyDoc_STRVAR(py_SMBConf_type_doc,
+"SMBConf objects provide uniform access to Samba configuration backends.\n"
+"\n"
+"The SMBConf type should not be instantiated directly. Rather, use a\n"
+"backend specific init function like init_txt.\n");
+
+static PyTypeObject py_SMBConf_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "smbconf.SMBConf",
+ .tp_doc = py_SMBConf_type_doc,
+ .tp_basicsize = sizeof(py_SMBConf_Object),
+ .tp_methods = py_smbconf_obj_methods,
+ .tp_new = obj_new,
+ .tp_dealloc = (destructor) obj_dealloc,
+};
+
+static PyObject *py_init_txt(PyObject * module, PyObject * args)
+{
+ py_SMBConf_Object *obj;
+ sbcErr err;
+ char *path = NULL;
+ struct smbconf_ctx *conf_ctx = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &path)) {
+ return NULL;
+ }
+
+ obj = (py_SMBConf_Object *) obj_new(&py_SMBConf_Type, NULL, NULL);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ err = smbconf_init_txt(obj->mem_ctx, &conf_ctx, path);
+ if (err != SBC_ERR_OK) {
+ Py_DECREF(obj);
+ py_raise_SMBConfError(err);
+ return NULL;
+ }
+ obj->conf_ctx = conf_ctx;
+ return (PyObject *) obj;
+}
+
+static PyObject *py_smbconf_error(PyObject * module, PyObject * args)
+{
+ sbcErr errcode;
+
+ if (!PyArg_ParseTuple(args, "i", &errcode)) {
+ return NULL;
+ }
+
+ /* this always raises an exception. it doesn't return the exception. */
+ py_raise_SMBConfError(errcode);
+ return NULL;
+}
+
+static PyMethodDef pysmbconf_methods[] = {
+ { "init_txt", (PyCFunction) py_init_txt, METH_VARARGS,
+ "Return an SMBConf object for the given text config file." },
+ { "_smbconf_error", (PyCFunction) py_smbconf_error, METH_VARARGS,
+ "Raise an SMBConfError based on the given error code." },
+ { 0 },
+};
+
+PyDoc_STRVAR(py_smbconf_doc,
+"The smbconf module is a wrapper for Samba's smbconf library.\n"
+"This library supports common functions to access the contents\n"
+"of a configuration backend, such as the text-based smb.conf file\n"
+"or the read-write registry backend.\n"
+"The read-only functions on the SMBConf type function on both backend\n"
+"types. Future, write based functions need a writable backend (registry).\n"
+"\n"
+"Note that the registry backend will be provided by a different\n"
+"library module from the source3 tree (implementation TBD).\n");
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "smbconf",
+ .m_doc = py_smbconf_doc,
+ .m_size = -1,
+ .m_methods = pysmbconf_methods,
+};
+
+MODULE_INIT_FUNC(smbconf)
+{
+ PyObject *m = PyModule_Create(&moduledef);
+ if (m == NULL) {
+ return NULL;
+ }
+
+ if (PyType_Ready(&py_SMBConf_Type) < 0) {
+ Py_DECREF(m);
+ return NULL;
+ }
+ Py_INCREF(&py_SMBConf_Type);
+ if (PyModule_AddObject(m, "SMBConf", (PyObject *) & py_SMBConf_Type) <
+ 0) {
+ Py_DECREF(&py_SMBConf_Type);
+ Py_DECREF(m);
+ return NULL;
+ }
+
+ PyExc_SMBConfError =
+ PyErr_NewException(discard_const_p(char, "smbconf.SMBConfError"),
+ NULL, NULL);
+ if (PyExc_SMBConfError == NULL) {
+ Py_DECREF(m);
+ return NULL;
+ }
+ Py_INCREF(PyExc_SMBConfError);
+ if (PyModule_AddObject(m, "SMBConfError", PyExc_SMBConfError) < 0) {
+ Py_DECREF(PyExc_SMBConfError);
+ Py_DECREF(m);
+ return NULL;
+ }
+
+/*
+ * ADD_FLAGS macro borrowed from source3/libsmb/pylibsmb.c
+ */
+#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val))
+
+ ADD_FLAGS(SBC_ERR_OK);
+ ADD_FLAGS(SBC_ERR_NOT_IMPLEMENTED);
+ ADD_FLAGS(SBC_ERR_NOT_SUPPORTED);
+ ADD_FLAGS(SBC_ERR_UNKNOWN_FAILURE);
+ ADD_FLAGS(SBC_ERR_NOMEM);
+ ADD_FLAGS(SBC_ERR_INVALID_PARAM);
+ ADD_FLAGS(SBC_ERR_BADFILE);
+ ADD_FLAGS(SBC_ERR_NO_SUCH_SERVICE);
+ ADD_FLAGS(SBC_ERR_IO_FAILURE);
+ ADD_FLAGS(SBC_ERR_CAN_NOT_COMPLETE);
+ ADD_FLAGS(SBC_ERR_NO_MORE_ITEMS);
+ ADD_FLAGS(SBC_ERR_FILE_EXISTS);
+ ADD_FLAGS(SBC_ERR_ACCESS_DENIED);
+
+ return m;
+}
diff --git a/lib/smbconf/pysmbconf.h b/lib/smbconf/pysmbconf.h
new file mode 100644
index 0000000..1e57bfb
--- /dev/null
+++ b/lib/smbconf/pysmbconf.h
@@ -0,0 +1,36 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library - Python bindings
+ *
+ * Copyright (C) John Mulligan <phlogistonjohn@asynchrono.us> 2022
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PYSMBCONF_H_
+#define _PYSMBCONF_H_
+
+#include "lib/replace/system/python.h"
+#include "lib/smbconf/smbconf.h"
+
+typedef struct {
+ PyObject_HEAD
+
+ /* C values embedded in our python type */
+ TALLOC_CTX * mem_ctx;
+ struct smbconf_ctx *conf_ctx;
+} py_SMBConf_Object;
+
+
+#endif /* _PYSMBCONF_H_ */
diff --git a/lib/smbconf/smbconf.c b/lib/smbconf/smbconf.c
new file mode 100644
index 0000000..4129ea5
--- /dev/null
+++ b/lib/smbconf/smbconf.c
@@ -0,0 +1,511 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library
+ * Copyright (C) Michael Adam 2007-2008
+ * Copyright (C) Guenther Deschner 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbconf_private.h"
+
+/**********************************************************************
+ *
+ * internal helper functions
+ *
+ **********************************************************************/
+
+static sbcErr smbconf_global_check(struct smbconf_ctx *ctx)
+{
+ if (!smbconf_share_exists(ctx, GLOBAL_NAME)) {
+ return smbconf_create_share(ctx, GLOBAL_NAME);
+ }
+
+ return SBC_ERR_OK;
+}
+
+
+/**********************************************************************
+ *
+ * The actual libsmbconf API functions that are exported.
+ *
+ **********************************************************************/
+
+const char *sbcErrorString(sbcErr error)
+{
+ switch (error) {
+ case SBC_ERR_OK:
+ return "SBC_ERR_OK";
+ case SBC_ERR_NOT_IMPLEMENTED:
+ return "SBC_ERR_NOT_IMPLEMENTED";
+ case SBC_ERR_NOT_SUPPORTED:
+ return "SBC_ERR_NOT_SUPPORTED";
+ case SBC_ERR_UNKNOWN_FAILURE:
+ return "SBC_ERR_UNKNOWN_FAILURE";
+ case SBC_ERR_NOMEM:
+ return "SBC_ERR_NOMEM";
+ case SBC_ERR_INVALID_PARAM:
+ return "SBC_ERR_INVALID_PARAM";
+ case SBC_ERR_BADFILE:
+ return "SBC_ERR_BADFILE";
+ case SBC_ERR_NO_SUCH_SERVICE:
+ return "SBC_ERR_NO_SUCH_SERVICE";
+ case SBC_ERR_IO_FAILURE:
+ return "SBC_ERR_IO_FAILURE";
+ case SBC_ERR_CAN_NOT_COMPLETE:
+ return "SBC_ERR_CAN_NOT_COMPLETE";
+ case SBC_ERR_NO_MORE_ITEMS:
+ return "SBC_ERR_NO_MORE_ITEMS";
+ case SBC_ERR_FILE_EXISTS:
+ return "SBC_ERR_FILE_EXISTS";
+ case SBC_ERR_ACCESS_DENIED:
+ return "SBC_ERR_ACCESS_DENIED";
+ }
+
+ return "unknown sbcErr value";
+}
+
+
+/**
+ * Tell whether the backend requires messaging to be set up
+ * for the backend to work correctly.
+ */
+bool smbconf_backend_requires_messaging(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->requires_messaging(ctx);
+}
+
+/**
+ * Tell whether the source is writeable.
+ */
+bool smbconf_is_writeable(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->is_writeable(ctx);
+}
+
+/**
+ * Close the configuration.
+ */
+void smbconf_shutdown(struct smbconf_ctx *ctx)
+{
+ talloc_free(ctx);
+}
+
+/**
+ * Detect changes in the configuration.
+ * The given csn struct is filled with the current csn.
+ * smbconf_changed() can also be used for initial retrieval
+ * of the csn.
+ */
+bool smbconf_changed(struct smbconf_ctx *ctx, struct smbconf_csn *csn,
+ const char *service, const char *param)
+{
+ struct smbconf_csn old_csn;
+
+ if (csn == NULL) {
+ return false;
+ }
+
+ old_csn = *csn;
+
+ ctx->ops->get_csn(ctx, csn, service, param);
+ return (csn->csn != old_csn.csn);
+}
+
+/**
+ * Drop the whole configuration (restarting empty).
+ */
+sbcErr smbconf_drop(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->drop(ctx);
+}
+
+/**
+ * Get the whole configuration as lists of strings with counts:
+ *
+ * num_shares : number of shares
+ * share_names : list of length num_shares of share names
+ * num_params : list of length num_shares of parameter counts for each share
+ * param_names : list of lists of parameter names for each share
+ * param_values : list of lists of parameter values for each share
+ */
+sbcErr smbconf_get_config(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_shares,
+ struct smbconf_service ***services)
+{
+ sbcErr err;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint32_t tmp_num_shares;
+ char **tmp_share_names;
+ struct smbconf_service **tmp_services;
+ uint32_t count;
+
+ if ((num_shares == NULL) || (services == NULL)) {
+ err = SBC_ERR_INVALID_PARAM;
+ goto done;
+ }
+
+ tmp_ctx = talloc_stackframe();
+
+ err = smbconf_get_share_names(ctx, tmp_ctx, &tmp_num_shares,
+ &tmp_share_names);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+
+ tmp_services = talloc_array(tmp_ctx, struct smbconf_service *,
+ tmp_num_shares);
+ if (tmp_services == NULL) {
+ err = SBC_ERR_NOMEM;
+ goto done;
+ }
+
+ for (count = 0; count < tmp_num_shares; count++) {
+ err = smbconf_get_share(ctx, tmp_services,
+ tmp_share_names[count],
+ &tmp_services[count]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ }
+
+ err = SBC_ERR_OK;
+
+ *num_shares = tmp_num_shares;
+ if (tmp_num_shares > 0) {
+ *services = talloc_move(mem_ctx, &tmp_services);
+ } else {
+ *services = NULL;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return err;
+}
+
+/**
+ * get the list of share names defined in the configuration.
+ */
+sbcErr smbconf_get_share_names(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_shares,
+ char ***share_names)
+{
+ return ctx->ops->get_share_names(ctx, mem_ctx, num_shares,
+ share_names);
+}
+
+/**
+ * check if a share/service of a given name exists
+ */
+bool smbconf_share_exists(struct smbconf_ctx *ctx,
+ const char *servicename)
+{
+ return ctx->ops->share_exists(ctx, servicename);
+}
+
+/**
+ * Add a service if it does not already exist.
+ */
+sbcErr smbconf_create_share(struct smbconf_ctx *ctx,
+ const char *servicename)
+{
+ if ((servicename != NULL) && smbconf_share_exists(ctx, servicename)) {
+ return SBC_ERR_FILE_EXISTS;
+ }
+
+ return ctx->ops->create_share(ctx, servicename);
+}
+
+/**
+ * create and set the definition for a new share (service).
+ */
+sbcErr smbconf_create_set_share(struct smbconf_ctx *ctx,
+ struct smbconf_service *service)
+{
+ sbcErr err, err2;
+ int i;
+ uint32_t num_includes = 0;
+ char **includes = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ if ((service->name != NULL) && smbconf_share_exists(ctx, service->name))
+ {
+ return SBC_ERR_FILE_EXISTS;
+ }
+
+ err = smbconf_transaction_start(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ tmp_ctx = talloc_stackframe();
+
+ err = smbconf_create_share(ctx, service->name);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto cancel;
+ }
+
+ for (i = 0; i < service->num_params; i++) {
+ if (strequal(service->param_names[i], "include")) {
+ includes = talloc_realloc(tmp_ctx, includes, char *,
+ num_includes+1);
+ if (includes == NULL) {
+ err = SBC_ERR_NOMEM;
+ goto cancel;
+ }
+ includes[num_includes] = talloc_strdup(includes,
+ service->param_values[i]);
+ if (includes[num_includes] == NULL) {
+ err = SBC_ERR_NOMEM;
+ goto cancel;
+ }
+ num_includes++;
+ } else {
+ err = smbconf_set_parameter(ctx,
+ service->name,
+ service->param_names[i],
+ service->param_values[i]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto cancel;
+ }
+ }
+ }
+
+ err = smbconf_set_includes(ctx, service->name, num_includes,
+ discard_const_p(const char *, includes));
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto cancel;
+ }
+
+ err = smbconf_transaction_commit(ctx);
+
+ goto done;
+
+cancel:
+ err2 = smbconf_transaction_cancel(ctx);
+ if (!SBC_ERROR_IS_OK(err2)) {
+ DEBUG(5, (__location__ ": Error cancelling transaction: %s\n",
+ sbcErrorString(err2)));
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return err;
+}
+
+/**
+ * get a definition of a share (service) from configuration.
+ */
+sbcErr smbconf_get_share(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *servicename,
+ struct smbconf_service **service)
+{
+ return ctx->ops->get_share(ctx, mem_ctx, servicename, service);
+}
+
+/**
+ * delete a service from configuration
+ */
+sbcErr smbconf_delete_share(struct smbconf_ctx *ctx, const char *servicename)
+{
+ if (!smbconf_share_exists(ctx, servicename)) {
+ return SBC_ERR_NO_SUCH_SERVICE;
+ }
+
+ return ctx->ops->delete_share(ctx, servicename);
+}
+
+/**
+ * set a configuration parameter to the value provided.
+ */
+sbcErr smbconf_set_parameter(struct smbconf_ctx *ctx,
+ const char *service,
+ const char *param,
+ const char *valstr)
+{
+ return ctx->ops->set_parameter(ctx, service, param, valstr);
+}
+
+/**
+ * Set a global parameter
+ * (i.e. a parameter in the [global] service).
+ *
+ * This also creates [global] when it does not exist.
+ */
+sbcErr smbconf_set_global_parameter(struct smbconf_ctx *ctx,
+ const char *param, const char *val)
+{
+ sbcErr err;
+
+ err = smbconf_global_check(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+ err = smbconf_set_parameter(ctx, GLOBAL_NAME, param, val);
+
+ return err;
+}
+
+/**
+ * get the value of a configuration parameter as a string
+ */
+sbcErr smbconf_get_parameter(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ const char *param,
+ char **valstr)
+{
+ if (valstr == NULL) {
+ return SBC_ERR_INVALID_PARAM;
+ }
+
+ return ctx->ops->get_parameter(ctx, mem_ctx, service, param, valstr);
+}
+
+/**
+ * Get the value of a global parameter.
+ *
+ * Create [global] if it does not exist.
+ */
+sbcErr smbconf_get_global_parameter(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *param,
+ char **valstr)
+{
+ sbcErr err;
+
+ err = smbconf_global_check(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ err = smbconf_get_parameter(ctx, mem_ctx, GLOBAL_NAME, param,
+ valstr);
+
+ return err;
+}
+
+/**
+ * delete a parameter from configuration
+ */
+sbcErr smbconf_delete_parameter(struct smbconf_ctx *ctx,
+ const char *service, const char *param)
+{
+ return ctx->ops->delete_parameter(ctx, service, param);
+}
+
+/**
+ * Delete a global parameter.
+ *
+ * Create [global] if it does not exist.
+ */
+sbcErr smbconf_delete_global_parameter(struct smbconf_ctx *ctx,
+ const char *param)
+{
+ sbcErr err;
+
+ err = smbconf_global_check(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+ err = smbconf_delete_parameter(ctx, GLOBAL_NAME, param);
+
+ return err;
+}
+
+sbcErr smbconf_get_includes(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ uint32_t *num_includes, char ***includes)
+{
+ return ctx->ops->get_includes(ctx, mem_ctx, service, num_includes,
+ includes);
+}
+
+sbcErr smbconf_get_global_includes(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_includes, char ***includes)
+{
+ sbcErr err;
+
+ err = smbconf_global_check(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+ err = smbconf_get_includes(ctx, mem_ctx, GLOBAL_NAME,
+ num_includes, includes);
+
+ return err;
+}
+
+sbcErr smbconf_set_includes(struct smbconf_ctx *ctx,
+ const char *service,
+ uint32_t num_includes, const char **includes)
+{
+ return ctx->ops->set_includes(ctx, service, num_includes, includes);
+}
+
+sbcErr smbconf_set_global_includes(struct smbconf_ctx *ctx,
+ uint32_t num_includes,
+ const char **includes)
+{
+ sbcErr err;
+
+ err = smbconf_global_check(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+ err = smbconf_set_includes(ctx, GLOBAL_NAME,
+ num_includes, includes);
+
+ return err;
+}
+
+
+sbcErr smbconf_delete_includes(struct smbconf_ctx *ctx, const char *service)
+{
+ return ctx->ops->delete_includes(ctx, service);
+}
+
+sbcErr smbconf_delete_global_includes(struct smbconf_ctx *ctx)
+{
+ sbcErr err;
+
+ err = smbconf_global_check(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+ err = smbconf_delete_includes(ctx, GLOBAL_NAME);
+
+ return err;
+}
+
+sbcErr smbconf_transaction_start(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->transaction_start(ctx);
+}
+
+sbcErr smbconf_transaction_commit(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->transaction_commit(ctx);
+}
+
+sbcErr smbconf_transaction_cancel(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->transaction_cancel(ctx);
+}
diff --git a/lib/smbconf/smbconf.h b/lib/smbconf/smbconf.h
new file mode 100644
index 0000000..e5f138f
--- /dev/null
+++ b/lib/smbconf/smbconf.h
@@ -0,0 +1,505 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMBCONF_H__
+#define __LIBSMBCONF_H__
+
+/**
+ * @defgroup libsmbconf The smbconf API
+ *
+ * libsmbconf is a library to read or, based on the backend, modify the Samba
+ * configuration.
+ *
+ * @{
+ */
+
+/**
+ * @brief Status codes returned from smbconf functions
+ */
+enum _sbcErrType {
+ SBC_ERR_OK = 0, /**< Successful completion **/
+ SBC_ERR_NOT_IMPLEMENTED, /**< Function not implemented **/
+ SBC_ERR_NOT_SUPPORTED, /**< Function not supported **/
+ SBC_ERR_UNKNOWN_FAILURE, /**< General failure **/
+ SBC_ERR_NOMEM, /**< Memory allocation error **/
+ SBC_ERR_INVALID_PARAM, /**< An Invalid parameter was supplied **/
+ SBC_ERR_BADFILE, /**< A bad file was supplied **/
+ SBC_ERR_NO_SUCH_SERVICE, /**< There is no such service provided **/
+ SBC_ERR_IO_FAILURE, /**< There was an IO error **/
+ SBC_ERR_CAN_NOT_COMPLETE,/**< Can not complete action **/
+ SBC_ERR_NO_MORE_ITEMS, /**< No more items left **/
+ SBC_ERR_FILE_EXISTS, /**< File already exists **/
+ SBC_ERR_ACCESS_DENIED, /**< Access has been denied **/
+};
+
+typedef enum _sbcErrType sbcErr;
+
+#define SBC_ERROR_IS_OK(x) ((x) == SBC_ERR_OK)
+#define SBC_ERROR_EQUAL(x,y) ((x) == (y))
+
+struct smbconf_ctx;
+
+/* the change sequence number */
+struct smbconf_csn {
+ uint64_t csn;
+};
+
+/** Information about a service */
+struct smbconf_service {
+ char *name; /**< The name of the share */
+ uint32_t num_params; /**< List of length num_shares of parameter counts for each share */
+ char **param_names; /**< List of lists of parameter names for each share */
+ char **param_values; /**< List of lists of parameter values for each share */
+};
+
+/*
+ * The smbconf API functions
+ */
+
+/**
+ * @brief Translate an error value into a string
+ *
+ * @param error
+ *
+ * @return a pointer to a static string
+ **/
+const char *sbcErrorString(sbcErr error);
+
+/**
+ * @brief Check if the backend requires messaging to be set up.
+ *
+ * Tell whether the backend requires messaging to be set up
+ * for the backend to work correctly.
+ *
+ * @param[in] ctx The smbconf context to check.
+ *
+ * @return True if needed, false if not.
+ */
+bool smbconf_backend_requires_messaging(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Tell whether the source is writeable.
+ *
+ * @param[in] ctx The smbconf context to check.
+ *
+ * @return True if it is writeable, false if not.
+ */
+bool smbconf_is_writeable(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Close the configuration.
+ *
+ * @param[in] ctx The smbconf context to close.
+ */
+void smbconf_shutdown(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Detect changes in the configuration.
+ *
+ * Get the change sequence number of the given service/parameter. Service and
+ * parameter strings may be NULL.
+ *
+ * The given change sequence number (csn) struct is filled with the current
+ * csn. smbconf_changed() can also be used for initial retrieval of the csn.
+ *
+ * @param[in] ctx The smbconf context to check for changes.
+ *
+ * @param[inout] csn The smbconf csn to be filled.
+ *
+ * @param[in] service The service name to check or NULL.
+ *
+ * @param[in] param The param to check or NULL.
+ *
+ * @return True if it has been changed, false if not.
+ */
+bool smbconf_changed(struct smbconf_ctx *ctx, struct smbconf_csn *csn,
+ const char *service, const char *param);
+
+/**
+ * @brief Drop the whole configuration (restarting empty).
+ *
+ * @param[in] ctx The smbconf context to drop the config.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_drop(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Get the whole configuration as lists of strings with counts.
+ *
+ * @param[in] ctx The smbconf context to get the lists from.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] num_shares A pointer to store the number of shares.
+ *
+ * @param[out] services A pointer to store the services.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ *
+ * @see smbconf_service
+ */
+sbcErr smbconf_get_config(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_shares,
+ struct smbconf_service ***services);
+
+/**
+ * @brief Get the list of share names defined in the configuration.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] mem_ctx The memory context to use.
+ *
+ * @param[in] num_shares A pointer to store the number of shares.
+ *
+ * @param[in] share_names A pointer to store the share names.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_get_share_names(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_shares,
+ char ***share_names);
+
+/**
+ * @brief Check if a share/service of a given name exists.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] servicename The service name to check if it exists.
+ *
+ * @return True if it exists, false if not.
+ */
+bool smbconf_share_exists(struct smbconf_ctx *ctx, const char *servicename);
+
+/**
+ * @brief Add a service if it does not already exist.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] servicename The name of the service to add.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_create_share(struct smbconf_ctx *ctx, const char *servicename);
+
+/**
+ * @brief create and set the definition for a new service.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] service The definition for the added service.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_create_set_share(struct smbconf_ctx *ctx,
+ struct smbconf_service *service);
+
+/**
+ * @brief Get a definition of a share (service) from configuration.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] mem_ctx A memory context to allocate the result.
+ *
+ * @param[in] servicename The service name to get the information from.
+ *
+ * @param[out] service A pointer to store the service information about the
+ * share.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ *
+ * @see smbconf_service
+ */
+sbcErr smbconf_get_share(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *servicename,
+ struct smbconf_service **service);
+
+/**
+ * @brief Delete a service from configuration.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] servicename The service name to delete.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_delete_share(struct smbconf_ctx *ctx,
+ const char *servicename);
+
+/**
+ * @brief Set a configuration parameter to the value provided.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] service The service name to set the parameter.
+ *
+ * @param[in] param The name of the parameter to set.
+ *
+ * @param[in] valstr The value to set.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_set_parameter(struct smbconf_ctx *ctx,
+ const char *service,
+ const char *param,
+ const char *valstr);
+
+/**
+ * @brief Set a global configuration parameter to the value provided.
+ *
+ * This adds a paramet in the [global] service. It also creates [global] if it
+ * doesn't exist.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] param The name of the parameter to set.
+ *
+ * @param[in] val The value to set.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_set_global_parameter(struct smbconf_ctx *ctx,
+ const char *param, const char *val);
+
+/**
+ * @brief Get the value of a configuration parameter as a string.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] mem_ctx The memory context to allocate the string on.
+ *
+ * @param[in] service The name of the service where to find the parameter.
+ *
+ * @param[in] param The parameter to get.
+ *
+ * @param[out] valstr A pointer to store the value as a string.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_get_parameter(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ const char *param,
+ char **valstr);
+
+/**
+ * @brief Get the value of a global configuration parameter as a string.
+ *
+ * It also creates [global] if it doesn't exist.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] mem_ctx The memory context to allocate the string on.
+ *
+ * @param[in] param The parameter to get.
+ *
+ * @param[out] valstr A pointer to store the value as a string.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_get_global_parameter(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *param,
+ char **valstr);
+
+/**
+ * @brief Delete a parameter from the configuration.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] service The service where the parameter can be found.
+ *
+ * @param[in] param The name of the parameter to delete.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_delete_parameter(struct smbconf_ctx *ctx,
+ const char *service, const char *param);
+
+/**
+ * @brief Delete a global parameter from the configuration.
+ *
+ * It also creates [global] if it doesn't exist.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] param The name of the parameter to delete.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_delete_global_parameter(struct smbconf_ctx *ctx,
+ const char *param);
+
+/**
+ * @brief Get the list of names of included files.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] mem_ctx The memory context to allocate the names.
+ *
+ * @param[in] service The service name to get the include files.
+ *
+ * @param[out] num_includes A pointer to store the number of included files.
+ *
+ * @param[out] includes A pointer to store the paths of the included files.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_get_includes(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ uint32_t *num_includes, char ***includes);
+
+/**
+ * @brief Get the list of globally included files.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] mem_ctx The memory context to allocate the names.
+ *
+ * @param[out] num_includes A pointer to store the number of included files.
+ *
+ * @param[out] includes A pointer to store the paths of the included files.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_get_global_includes(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_includes, char ***includes);
+
+/**
+ * @brief Set a list of config files to include on the given service.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] service The service to add includes.
+ *
+ * @param[in] num_includes The number of includes to set.
+ *
+ * @param[in] includes A list of paths to include.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_set_includes(struct smbconf_ctx *ctx,
+ const char *service,
+ uint32_t num_includes, const char **includes);
+
+/**
+ * @brief Set a list of config files to include globally.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] num_includes The number of includes to set.
+ *
+ * @param[in] includes A list of paths to include.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_set_global_includes(struct smbconf_ctx *ctx,
+ uint32_t num_includes,
+ const char **includes);
+
+/**
+ * @brief Delete include parameter on the given service.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @param[in] service The name of the service to delete the includes from.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_delete_includes(struct smbconf_ctx *ctx, const char *service);
+
+/**
+ * @brief Delete include parameter from the global service.
+ *
+ * @param[in] ctx The smbconf context to use.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_delete_global_includes(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Start a transaction on the configuration backend.
+ *
+ * Transactions are exposed in order to make it possible
+ * to create atomic compound writing commands.
+ *
+ * @param[in] ctx The smbconf context to start the transaction.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ */
+sbcErr smbconf_transaction_start(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Commit a transaction on the configuration backend.
+ *
+ * Transactions are exposed in order to make it possible
+ * to create atomic compound writing commands.
+ *
+ * @param[in] ctx The smbconf context to commit the transaction.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ *
+ * @see smbconf_transaction_start()
+ */
+sbcErr smbconf_transaction_commit(struct smbconf_ctx *ctx);
+
+/**
+ * @brief Cancel a transaction on the configuration backend.
+ *
+ * Transactions are exposed in order to make it possible
+ * to create atomic compound writing commands.
+ *
+ * @param[in] ctx The smbconf context to cancel the transaction.
+ *
+ * @return SBC_ERR_OK on success, a corresponding sbcErr if an
+ * error occurred.
+ *
+ * @see smbconf_transaction_start()
+ */
+sbcErr smbconf_transaction_cancel(struct smbconf_ctx *ctx);
+
+/* @} ******************************************************************/
+
+#endif /* _LIBSMBCONF_H_ */
diff --git a/lib/smbconf/smbconf_private.h b/lib/smbconf/smbconf_private.h
new file mode 100644
index 0000000..e768c30
--- /dev/null
+++ b/lib/smbconf/smbconf_private.h
@@ -0,0 +1,96 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMBCONF_PRIVATE_H__
+#define __LIBSMBCONF_PRIVATE_H__
+
+#ifndef GLOBAL_NAME
+#define GLOBAL_NAME "global"
+#endif
+
+#include "lib/smbconf/smbconf.h"
+
+struct smbconf_ops {
+ sbcErr (*init)(struct smbconf_ctx *ctx, const char *path);
+ int (*shutdown)(struct smbconf_ctx *ctx);
+ bool (*requires_messaging)(struct smbconf_ctx *ctx);
+ bool (*is_writeable)(struct smbconf_ctx *ctx);
+ sbcErr (*open_conf)(struct smbconf_ctx *ctx);
+ int (*close_conf)(struct smbconf_ctx *ctx);
+ void (*get_csn)(struct smbconf_ctx *ctx, struct smbconf_csn *csn,
+ const char *service, const char *param);
+ sbcErr (*drop)(struct smbconf_ctx *ctx);
+ sbcErr (*get_share_names)(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_shares,
+ char ***share_names);
+ bool (*share_exists)(struct smbconf_ctx *ctx, const char *service);
+ sbcErr (*create_share)(struct smbconf_ctx *ctx, const char *service);
+ sbcErr (*get_share)(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *servicename,
+ struct smbconf_service **service);
+ sbcErr (*delete_share)(struct smbconf_ctx *ctx,
+ const char *servicename);
+ sbcErr (*set_parameter)(struct smbconf_ctx *ctx,
+ const char *service,
+ const char *param,
+ const char *valstr);
+ sbcErr (*get_parameter)(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ const char *param,
+ char **valstr);
+ sbcErr (*delete_parameter)(struct smbconf_ctx *ctx,
+ const char *service, const char *param);
+ sbcErr (*get_includes)(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ uint32_t *num_includes, char ***includes);
+ sbcErr (*set_includes)(struct smbconf_ctx *ctx,
+ const char *service,
+ uint32_t num_includes, const char **includes);
+ sbcErr (*delete_includes)(struct smbconf_ctx *ctx,
+ const char *service);
+ sbcErr (*transaction_start)(struct smbconf_ctx *ctx);
+ sbcErr (*transaction_commit)(struct smbconf_ctx *ctx);
+ sbcErr (*transaction_cancel)(struct smbconf_ctx *ctx);
+};
+
+struct smbconf_ctx {
+ const char *path;
+ struct smbconf_ops *ops;
+ void *data; /* private data for use in backends */
+};
+
+sbcErr smbconf_init_internal(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx,
+ const char *path, struct smbconf_ops *ops);
+
+sbcErr smbconf_add_string_to_array(TALLOC_CTX *mem_ctx,
+ char ***array,
+ uint32_t count,
+ const char *string);
+
+bool smbconf_find_in_array(const char *string, char **list,
+ uint32_t num_entries, uint32_t *entry);
+
+bool smbconf_reverse_find_in_array(const char *string, char **list,
+ uint32_t num_entries, uint32_t *entry);
+
+#endif
diff --git a/lib/smbconf/smbconf_txt.c b/lib/smbconf/smbconf_txt.c
new file mode 100644
index 0000000..70a35ec
--- /dev/null
+++ b/lib/smbconf/smbconf_txt.c
@@ -0,0 +1,682 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library, text backend
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This is a sample implementation of a libsmbconf text backend
+ * using the params.c parser.
+ *
+ * It is read only.
+ * Don't expect brilliant performance, since it is not hashing the lists.
+ */
+
+#include "includes.h"
+#include "smbconf_private.h"
+#include "lib/smbconf/smbconf_txt.h"
+
+struct txt_cache {
+ uint32_t current_share;
+ uint32_t num_shares;
+ char **share_names;
+ uint32_t *num_params;
+ char ***param_names;
+ char ***param_values;
+};
+
+struct txt_private_data {
+ struct txt_cache *cache;
+ uint64_t csn;
+ bool verbatim;
+};
+
+/**********************************************************************
+ *
+ * helper functions
+ *
+ **********************************************************************/
+
+/**
+ * a convenience helper to cast the private data structure
+ */
+static struct txt_private_data *pd(struct smbconf_ctx *ctx)
+{
+ return (struct txt_private_data *)(ctx->data);
+}
+
+static bool smbconf_txt_do_section(const char *section, void *private_data)
+{
+ sbcErr err;
+ uint32_t idx;
+ struct txt_private_data *tpd = (struct txt_private_data *)private_data;
+ struct txt_cache *cache = tpd->cache;
+
+ if (smbconf_find_in_array(section, cache->share_names,
+ cache->num_shares, &idx))
+ {
+ cache->current_share = idx;
+ return true;
+ }
+
+ err = smbconf_add_string_to_array(cache, &(cache->share_names),
+ cache->num_shares, section);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return false;
+ }
+ cache->current_share = cache->num_shares;
+ cache->num_shares++;
+
+ cache->param_names = talloc_realloc(cache,
+ cache->param_names,
+ char **,
+ cache->num_shares);
+ if (cache->param_names == NULL) {
+ return false;
+ }
+ cache->param_names[cache->current_share] = NULL;
+
+ cache->param_values = talloc_realloc(cache,
+ cache->param_values,
+ char **,
+ cache->num_shares);
+ if (cache->param_values == NULL) {
+ return false;
+ }
+ cache->param_values[cache->current_share] = NULL;
+
+ cache->num_params = talloc_realloc(cache,
+ cache->num_params,
+ uint32_t,
+ cache->num_shares);
+ if (cache->num_params == NULL) {
+ return false;
+ }
+ cache->num_params[cache->current_share] = 0;
+
+ return true;
+}
+
+static bool smbconf_txt_do_parameter(const char *param_name,
+ const char *param_value,
+ void *private_data)
+{
+ sbcErr err;
+ char **param_names, **param_values;
+ uint32_t num_params;
+ uint32_t idx;
+ struct txt_private_data *tpd = (struct txt_private_data *)private_data;
+ struct txt_cache *cache = tpd->cache;
+
+ if (cache->num_shares == 0) {
+ /*
+ * not in any share yet,
+ * initialize the "empty" section (NULL):
+ * parameters without a previous [section] are stored here.
+ */
+ if (!smbconf_txt_do_section(NULL, private_data)) {
+ return false;
+ }
+ }
+
+ param_names = cache->param_names[cache->current_share];
+ param_values = cache->param_values[cache->current_share];
+ num_params = cache->num_params[cache->current_share];
+
+ if (!(tpd->verbatim) &&
+ smbconf_find_in_array(param_name, param_names, num_params, &idx))
+ {
+ talloc_free(param_values[idx]);
+ param_values[idx] = talloc_strdup(cache, param_value);
+ if (param_values[idx] == NULL) {
+ return false;
+ }
+ return true;
+ }
+ err = smbconf_add_string_to_array(cache,
+ &(cache->param_names[cache->current_share]),
+ num_params, param_name);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return false;
+ }
+ err = smbconf_add_string_to_array(cache,
+ &(cache->param_values[cache->current_share]),
+ num_params, param_value);
+ cache->num_params[cache->current_share]++;
+ return SBC_ERROR_IS_OK(err);
+}
+
+static void smbconf_txt_flush_cache(struct smbconf_ctx *ctx)
+{
+ talloc_free(pd(ctx)->cache);
+ pd(ctx)->cache = NULL;
+}
+
+static sbcErr smbconf_txt_init_cache(struct smbconf_ctx *ctx)
+{
+ if (pd(ctx)->cache != NULL) {
+ smbconf_txt_flush_cache(ctx);
+ }
+
+ pd(ctx)->cache = talloc_zero(pd(ctx), struct txt_cache);
+
+ if (pd(ctx)->cache == NULL) {
+ return SBC_ERR_NOMEM;
+ }
+
+ return SBC_ERR_OK;
+}
+
+static sbcErr smbconf_txt_load_file(struct smbconf_ctx *ctx)
+{
+ sbcErr err;
+ uint64_t new_csn;
+ int rc;
+ struct timespec mt = {0};
+
+ if (!file_exist(ctx->path)) {
+ return SBC_ERR_BADFILE;
+ }
+
+ rc = file_modtime(ctx->path, &mt);
+ if (rc != 0) {
+ /*
+ * Not worth mapping errno returned
+ * in rc to SBC_ERR_XXX. Just assume
+ * access denied.
+ */
+ return SBC_ERR_ACCESS_DENIED;
+ }
+ new_csn = (uint64_t)mt.tv_sec;
+ if (new_csn == pd(ctx)->csn) {
+ return SBC_ERR_OK;
+ }
+
+ err = smbconf_txt_init_cache(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ if (!pm_process(ctx->path, smbconf_txt_do_section,
+ smbconf_txt_do_parameter, pd(ctx)))
+ {
+ return SBC_ERR_CAN_NOT_COMPLETE;
+ }
+
+ pd(ctx)->csn = new_csn;
+
+ return SBC_ERR_OK;
+}
+
+
+/**********************************************************************
+ *
+ * smbconf operations: text backend implementations
+ *
+ **********************************************************************/
+
+/**
+ * initialize the text based smbconf backend
+ */
+static sbcErr smbconf_txt_init(struct smbconf_ctx *ctx, const char *path)
+{
+ if (path == NULL) {
+ return SBC_ERR_BADFILE;
+ }
+ ctx->path = talloc_strdup(ctx, path);
+ if (ctx->path == NULL) {
+ return SBC_ERR_NOMEM;
+ }
+
+ ctx->data = talloc_zero(ctx, struct txt_private_data);
+ if (ctx->data == NULL) {
+ return SBC_ERR_NOMEM;
+ }
+
+ pd(ctx)->verbatim = true;
+
+ return SBC_ERR_OK;
+}
+
+static int smbconf_txt_shutdown(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->close_conf(ctx);
+}
+
+static bool smbconf_txt_requires_messaging(struct smbconf_ctx *ctx)
+{
+ return false;
+}
+
+static bool smbconf_txt_is_writeable(struct smbconf_ctx *ctx)
+{
+ /* no write support in this backend yet... */
+ return false;
+}
+
+static sbcErr smbconf_txt_open(struct smbconf_ctx *ctx)
+{
+ return smbconf_txt_load_file(ctx);
+}
+
+static int smbconf_txt_close(struct smbconf_ctx *ctx)
+{
+ smbconf_txt_flush_cache(ctx);
+ return 0;
+}
+
+/**
+ * Get the change sequence number of the given service/parameter.
+ * service and parameter strings may be NULL.
+ */
+static void smbconf_txt_get_csn(struct smbconf_ctx *ctx,
+ struct smbconf_csn *csn,
+ const char *service, const char *param)
+{
+ struct timespec mt = {0};
+
+ if (csn == NULL) {
+ return;
+ }
+
+ (void)file_modtime(ctx->path, &mt);
+ csn->csn = (uint64_t)mt.tv_sec;
+}
+
+/**
+ * Drop the whole configuration (restarting empty)
+ */
+static sbcErr smbconf_txt_drop(struct smbconf_ctx *ctx)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+/**
+ * get the list of share names defined in the configuration.
+ */
+static sbcErr smbconf_txt_get_share_names(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_shares,
+ char ***share_names)
+{
+ uint32_t count;
+ uint32_t added_count = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ sbcErr err = SBC_ERR_OK;
+ char **tmp_share_names = NULL;
+
+ if ((num_shares == NULL) || (share_names == NULL)) {
+ return SBC_ERR_INVALID_PARAM;
+ }
+
+ err = smbconf_txt_load_file(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ tmp_ctx = talloc_stackframe();
+
+ /* make sure "global" is always listed first,
+ * possibly after NULL section */
+
+ if (smbconf_share_exists(ctx, NULL)) {
+ err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names,
+ 0, NULL);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ added_count++;
+ }
+
+ if (smbconf_share_exists(ctx, GLOBAL_NAME)) {
+ err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names,
+ added_count, GLOBAL_NAME);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ added_count++;
+ }
+
+ for (count = 0; count < pd(ctx)->cache->num_shares; count++) {
+ if (strequal(pd(ctx)->cache->share_names[count], GLOBAL_NAME) ||
+ (pd(ctx)->cache->share_names[count] == NULL))
+ {
+ continue;
+ }
+
+ err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names,
+ added_count,
+ pd(ctx)->cache->share_names[count]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ added_count++;
+ }
+
+ *num_shares = added_count;
+ if (added_count > 0) {
+ *share_names = talloc_move(mem_ctx, &tmp_share_names);
+ } else {
+ *share_names = NULL;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return err;
+}
+
+/**
+ * check if a share/service of a given name exists
+ */
+static bool smbconf_txt_share_exists(struct smbconf_ctx *ctx,
+ const char *servicename)
+{
+ sbcErr err;
+
+ err = smbconf_txt_load_file(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return false;
+ }
+
+ return smbconf_find_in_array(servicename,
+ pd(ctx)->cache->share_names,
+ pd(ctx)->cache->num_shares, NULL);
+}
+
+/**
+ * Add a service if it does not already exist
+ */
+static sbcErr smbconf_txt_create_share(struct smbconf_ctx *ctx,
+ const char *servicename)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+/**
+ * get a definition of a share (service) from configuration.
+ */
+static sbcErr smbconf_txt_get_share(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *servicename,
+ struct smbconf_service **service)
+{
+ sbcErr err;
+ uint32_t sidx, count;
+ bool found;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct smbconf_service *tmp_service = NULL;
+
+ err = smbconf_txt_load_file(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ found = smbconf_find_in_array(servicename,
+ pd(ctx)->cache->share_names,
+ pd(ctx)->cache->num_shares,
+ &sidx);
+ if (!found) {
+ return SBC_ERR_NO_SUCH_SERVICE;
+ }
+
+ tmp_ctx = talloc_stackframe();
+
+ tmp_service = talloc_zero(tmp_ctx, struct smbconf_service);
+ if (tmp_service == NULL) {
+ err = SBC_ERR_NOMEM;
+ goto done;
+ }
+
+ if (servicename != NULL) {
+ tmp_service->name = talloc_strdup(tmp_service, servicename);
+ if (tmp_service->name == NULL) {
+ err = SBC_ERR_NOMEM;
+ goto done;
+ }
+ }
+
+ for (count = 0; count < pd(ctx)->cache->num_params[sidx]; count++) {
+ err = smbconf_add_string_to_array(tmp_service,
+ &(tmp_service->param_names),
+ count,
+ pd(ctx)->cache->param_names[sidx][count]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ err = smbconf_add_string_to_array(tmp_service,
+ &(tmp_service->param_values),
+ count,
+ pd(ctx)->cache->param_values[sidx][count]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ }
+
+ tmp_service->num_params = count;
+ *service = talloc_move(mem_ctx, &tmp_service);
+
+done:
+ talloc_free(tmp_ctx);
+ return err;
+}
+
+/**
+ * delete a service from configuration
+ */
+static sbcErr smbconf_txt_delete_share(struct smbconf_ctx *ctx,
+ const char *servicename)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+/**
+ * set a configuration parameter to the value provided.
+ */
+static sbcErr smbconf_txt_set_parameter(struct smbconf_ctx *ctx,
+ const char *service,
+ const char *param,
+ const char *valstr)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+/**
+ * get the value of a configuration parameter as a string
+ */
+static sbcErr smbconf_txt_get_parameter(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ const char *param,
+ char **valstr)
+{
+ sbcErr err;
+ bool found;
+ uint32_t share_index, param_index;
+
+ err = smbconf_txt_load_file(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ found = smbconf_find_in_array(service,
+ pd(ctx)->cache->share_names,
+ pd(ctx)->cache->num_shares,
+ &share_index);
+ if (!found) {
+ return SBC_ERR_NO_SUCH_SERVICE;
+ }
+
+ found = smbconf_reverse_find_in_array(param,
+ pd(ctx)->cache->param_names[share_index],
+ pd(ctx)->cache->num_params[share_index],
+ &param_index);
+ if (!found) {
+ return SBC_ERR_INVALID_PARAM;
+ }
+
+ *valstr = talloc_strdup(mem_ctx,
+ pd(ctx)->cache->param_values[share_index][param_index]);
+
+ if (*valstr == NULL) {
+ return SBC_ERR_NOMEM;
+ }
+
+ return SBC_ERR_OK;
+}
+
+/**
+ * delete a parameter from configuration
+ */
+static sbcErr smbconf_txt_delete_parameter(struct smbconf_ctx *ctx,
+ const char *service,
+ const char *param)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+static sbcErr smbconf_txt_get_includes(struct smbconf_ctx *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *service,
+ uint32_t *num_includes,
+ char ***includes)
+{
+ sbcErr err;
+ bool found;
+ uint32_t sidx, count;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint32_t tmp_num_includes = 0;
+ char **tmp_includes = NULL;
+
+ err = smbconf_txt_load_file(ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ found = smbconf_find_in_array(service,
+ pd(ctx)->cache->share_names,
+ pd(ctx)->cache->num_shares,
+ &sidx);
+ if (!found) {
+ return SBC_ERR_NO_SUCH_SERVICE;
+ }
+
+ tmp_ctx = talloc_stackframe();
+
+ for (count = 0; count < pd(ctx)->cache->num_params[sidx]; count++) {
+ if (strequal(pd(ctx)->cache->param_names[sidx][count],
+ "include"))
+ {
+ err = smbconf_add_string_to_array(tmp_ctx,
+ &tmp_includes,
+ tmp_num_includes,
+ pd(ctx)->cache->param_values[sidx][count]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ tmp_num_includes++;
+ }
+ }
+
+ *num_includes = tmp_num_includes;
+ if (*num_includes > 0) {
+ *includes = talloc_move(mem_ctx, &tmp_includes);
+ if (*includes == NULL) {
+ err = SBC_ERR_NOMEM;
+ goto done;
+ }
+ } else {
+ *includes = NULL;
+ }
+
+ err = SBC_ERR_OK;
+
+done:
+ talloc_free(tmp_ctx);
+ return err;
+}
+
+static sbcErr smbconf_txt_set_includes(struct smbconf_ctx *ctx,
+ const char *service,
+ uint32_t num_includes,
+ const char **includes)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+static sbcErr smbconf_txt_delete_includes(struct smbconf_ctx *ctx,
+ const char *service)
+{
+ return SBC_ERR_NOT_SUPPORTED;
+}
+
+static sbcErr smbconf_txt_transaction_start(struct smbconf_ctx *ctx)
+{
+ return SBC_ERR_OK;
+}
+
+static sbcErr smbconf_txt_transaction_commit(struct smbconf_ctx *ctx)
+{
+ return SBC_ERR_OK;
+}
+
+static sbcErr smbconf_txt_transaction_cancel(struct smbconf_ctx *ctx)
+{
+ return SBC_ERR_OK;
+}
+
+static struct smbconf_ops smbconf_ops_txt = {
+ .init = smbconf_txt_init,
+ .shutdown = smbconf_txt_shutdown,
+ .requires_messaging = smbconf_txt_requires_messaging,
+ .is_writeable = smbconf_txt_is_writeable,
+ .open_conf = smbconf_txt_open,
+ .close_conf = smbconf_txt_close,
+ .get_csn = smbconf_txt_get_csn,
+ .drop = smbconf_txt_drop,
+ .get_share_names = smbconf_txt_get_share_names,
+ .share_exists = smbconf_txt_share_exists,
+ .create_share = smbconf_txt_create_share,
+ .get_share = smbconf_txt_get_share,
+ .delete_share = smbconf_txt_delete_share,
+ .set_parameter = smbconf_txt_set_parameter,
+ .get_parameter = smbconf_txt_get_parameter,
+ .delete_parameter = smbconf_txt_delete_parameter,
+ .get_includes = smbconf_txt_get_includes,
+ .set_includes = smbconf_txt_set_includes,
+ .delete_includes = smbconf_txt_delete_includes,
+ .transaction_start = smbconf_txt_transaction_start,
+ .transaction_commit = smbconf_txt_transaction_commit,
+ .transaction_cancel = smbconf_txt_transaction_cancel,
+};
+
+
+/**
+ * initialize the smbconf text backend
+ * the only function that is exported from this module
+ */
+sbcErr smbconf_init_txt(TALLOC_CTX *mem_ctx,
+ struct smbconf_ctx **conf_ctx,
+ const char *path)
+{
+ sbcErr err;
+
+ err = smbconf_init_internal(mem_ctx, conf_ctx, path, &smbconf_ops_txt);
+ if (!SBC_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ return smbconf_txt_load_file(*conf_ctx);
+}
diff --git a/lib/smbconf/smbconf_txt.h b/lib/smbconf/smbconf_txt.h
new file mode 100644
index 0000000..72d6207
--- /dev/null
+++ b/lib/smbconf/smbconf_txt.h
@@ -0,0 +1,33 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library
+ * Copyright (C) Michael Adam 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMBCONF_TXT_H__
+#define __LIBSMBCONF_TXT_H__
+
+struct smbconf_ctx;
+
+/**
+ * initialization functions for the text/file backend modules
+ */
+
+sbcErr smbconf_init_txt(TALLOC_CTX *mem_ctx,
+ struct smbconf_ctx **conf_ctx,
+ const char *path);
+
+#endif /* _LIBSMBCONF_TXT_H_ */
diff --git a/lib/smbconf/smbconf_util.c b/lib/smbconf/smbconf_util.c
new file mode 100644
index 0000000..86a9598
--- /dev/null
+++ b/lib/smbconf/smbconf_util.c
@@ -0,0 +1,151 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * libsmbconf - Samba configuration library, utility functions
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbconf_private.h"
+
+
+static int smbconf_destroy_ctx(struct smbconf_ctx *ctx)
+{
+ return ctx->ops->shutdown(ctx);
+}
+
+/**
+ * Initialize the configuration.
+ *
+ * This should be the first function in a sequence of calls to smbconf
+ * functions:
+ *
+ * Upon success, this creates and returns the conf context
+ * that should be passed around in subsequent calls to the other
+ * smbconf functions.
+ *
+ * After the work with the configuration is completed, smbconf_shutdown()
+ * should be called.
+ */
+sbcErr smbconf_init_internal(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx,
+ const char *path, struct smbconf_ops *ops)
+{
+ sbcErr err = SBC_ERR_OK;
+ struct smbconf_ctx *ctx;
+
+ if (conf_ctx == NULL) {
+ return SBC_ERR_INVALID_PARAM;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct smbconf_ctx);
+ if (ctx == NULL) {
+ return SBC_ERR_NOMEM;
+ }
+
+ ctx->ops = ops;
+
+ err = ctx->ops->init(ctx, path);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto fail;
+ }
+
+ talloc_set_destructor(ctx, smbconf_destroy_ctx);
+
+ *conf_ctx = ctx;
+ return err;
+
+fail:
+ talloc_free(ctx);
+ return err;
+}
+
+
+/**
+ * add a string to a talloced array of strings.
+ */
+sbcErr smbconf_add_string_to_array(TALLOC_CTX *mem_ctx,
+ char ***array,
+ uint32_t count,
+ const char *string)
+{
+ char **new_array = NULL;
+
+ if (array == NULL) {
+ return SBC_ERR_INVALID_PARAM;
+ }
+
+ new_array = talloc_realloc(mem_ctx, *array, char *, count + 1);
+ if (new_array == NULL) {
+ return SBC_ERR_NOMEM;
+ }
+
+ if (string == NULL) {
+ new_array[count] = NULL;
+ } else {
+ new_array[count] = talloc_strdup(new_array, string);
+ if (new_array[count] == NULL) {
+ talloc_free(new_array);
+ return SBC_ERR_NOMEM;
+ }
+ }
+
+ *array = new_array;
+
+ return SBC_ERR_OK;
+}
+
+bool smbconf_find_in_array(const char *string, char **list,
+ uint32_t num_entries, uint32_t *entry)
+{
+ uint32_t i;
+
+ if (list == NULL) {
+ return false;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ if (((string == NULL) && (list[i] == NULL)) ||
+ strequal(string, list[i]))
+ {
+ if (entry != NULL) {
+ *entry = i;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool smbconf_reverse_find_in_array(const char *string, char **list,
+ uint32_t num_entries, uint32_t *entry)
+{
+ int32_t i;
+
+ if ((string == NULL) || (list == NULL) || (num_entries == 0)) {
+ return false;
+ }
+
+ for (i = num_entries - 1; i >= 0; i--) {
+ if (strequal(string, list[i])) {
+ if (entry != NULL) {
+ *entry = i;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/lib/smbconf/wscript_build b/lib/smbconf/wscript_build
new file mode 100644
index 0000000..97d6b18
--- /dev/null
+++ b/lib/smbconf/wscript_build
@@ -0,0 +1,10 @@
+bld.SAMBA_SUBSYSTEM('LIBSMBCONF',
+ source='smbconf.c smbconf_txt.c smbconf_util.c',
+ deps='talloc sendfile'
+ )
+
+bld.SAMBA3_PYTHON('pysmbconf',
+ source='pysmbconf.c',
+ public_deps=' '.join(['samba-util', 'tdb', 'talloc', 'smbconf']),
+ realname='samba/smbconf.so'
+ )