import HEAD into svn+ssh://svn.samba.org/home/svn/samba/trunk
[metze/old/v3-2-winbind-ndr.git] / source / python / py_tdb.c
1 /* 
2    Python wrappers for TDB module
3
4    Copyright (C) Tim Potter, 2002-2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /* 
22    NOTE: Since tdb is licenced under the GPL any program that uses these bindings
23    must be distributed under the GPL license terms since this is what
24    the GPL requires.
25
26    http://www.gnu.org/licenses/gpl-faq.html#IfInterpreterIsGPL 
27 */
28
29 #include "includes.h"
30
31 /* This symbol is used in both includes.h and Python.h which causes an
32    annoying compiler warning. */
33
34 #ifdef HAVE_FSTAT
35 #undef HAVE_FSTAT
36 #endif
37
38 #include "Python.h"
39
40 /* Tdb exception */
41
42 PyObject *py_tdb_error;
43
44 /* tdb handle object */
45
46 typedef struct {
47         PyObject_HEAD
48         TDB_CONTEXT *tdb;
49 } tdb_hnd_object;
50
51 PyTypeObject tdb_hnd_type;
52      
53 PyObject *new_tdb_hnd_object(TDB_CONTEXT *tdb)
54 {
55         tdb_hnd_object *obj;
56
57         obj = PyObject_New(tdb_hnd_object, &tdb_hnd_type);
58         obj->tdb = tdb;
59
60         return (PyObject *)obj;
61 }
62
63 PyObject *py_tdb_close(PyObject *self, PyObject *args)
64 {
65         tdb_hnd_object *obj;
66
67         if (!PyArg_ParseTuple(args, "O!", &tdb_hnd_type, &obj))
68                 return NULL;
69
70         if (tdb_close(obj->tdb) == -1) {
71                 obj->tdb = NULL;
72                 PyErr_SetString(py_tdb_error, strerror(errno));
73                 return NULL;
74         }
75
76         obj->tdb = NULL;
77
78         Py_INCREF(Py_None);
79         return Py_None;
80 }
81
82 PyObject *py_tdb_open(PyObject *self, PyObject *args, PyObject *kw)
83 {
84         static char *kwlist[] = { "name", "hash_size", "tdb_flags",
85                                   "open_flags", "mode", NULL };
86         char *name;
87         int hash_size = 0, flags = TDB_DEFAULT, open_flags = -1, open_mode = 0600;      
88         TDB_CONTEXT *tdb;
89
90         if (!PyArg_ParseTupleAndKeywords(
91                     args, kw, "s|iiii", kwlist, &name, &hash_size, &flags,
92                     &open_flags, &open_mode))
93                 return NULL;
94
95         /* Default open_flags to read/write */
96
97         if (open_flags == -1) {
98                 if (access(name, W_OK) == -1)
99                         open_flags = O_RDONLY;
100                 else
101                         open_flags = O_RDWR;
102         }
103
104         if (!(tdb = tdb_open(name, hash_size, flags, open_flags, open_mode))) {
105                 PyErr_SetString(py_tdb_error, strerror(errno));
106                 return NULL;
107         }
108
109         return new_tdb_hnd_object(tdb);
110 }
111
112 /*
113  * Allow a tdb to act as a python mapping (dictionary)
114  */
115
116 static int tdb_traverse_count(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
117                               void *state)
118 {
119         /* Do nothing - tdb_traverse will return the number of records
120            traversed. */
121
122         return 0;
123 }
124
125 static int tdb_hnd_length(tdb_hnd_object *obj)
126 {
127         int result;
128
129         result = tdb_traverse(obj->tdb, tdb_traverse_count, NULL);
130
131         return result;
132 }
133
134 static PyObject *tdb_hnd_subscript(tdb_hnd_object *obj, PyObject *key)
135 {
136         TDB_DATA drec, krec;
137         PyObject *result;
138
139         if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize))
140                 return NULL;
141
142         drec = tdb_fetch(obj->tdb, krec);
143
144         if (!drec.dptr) {
145                 PyErr_SetString(PyExc_KeyError,
146                                 PyString_AsString(key));
147                 return NULL;
148         }
149
150         result = PyString_FromStringAndSize(drec.dptr, drec.dsize);
151         free(drec.dptr);
152
153         return result;
154 }
155         
156 static int tdb_ass_subscript(tdb_hnd_object *obj, PyObject *key, PyObject *value)
157 {
158         TDB_DATA krec, drec;
159
160         if (!PyArg_Parse(key, "s#", &krec.dptr, &krec.dsize)) {
161                 PyErr_SetString(PyExc_TypeError,
162                                 "tdb mappings have string indices only");
163                 return -1;
164         }
165
166         if (!obj->tdb) {
167                 PyErr_SetString(
168                         py_tdb_error, "tdb object has been closed"); 
169                 return -1; 
170         }
171
172         if (!value) {
173
174                 /* Delete value */
175
176                 if (tdb_delete(obj->tdb, krec) == -1) {
177                         PyErr_SetString(PyExc_KeyError,
178                                         PyString_AsString(value));
179                         return -1;
180                 }
181
182         } else {
183
184                 /* Set value */
185
186                 if (!PyArg_Parse(value, "s#", &drec.dptr, &drec.dsize)) {
187                         PyErr_SetString(PyExc_TypeError,
188                                     "tdb mappings have string elements only");
189                         return -1;
190                 }
191
192                 errno = 0;
193
194                 if (tdb_store(obj->tdb, krec, drec, 0) < 0 ) {
195                         if (errno != 0)
196                                 PyErr_SetFromErrno(py_tdb_error);
197                         else
198                                 PyErr_SetString(
199                                         py_tdb_error, 
200                                         (char *)tdb_errorstr(obj->tdb));
201
202                         return -1;
203                 }
204         }
205
206         return 0;
207
208
209 static PyMappingMethods tdb_mapping = {
210         (inquiry) tdb_hnd_length,
211         (binaryfunc) tdb_hnd_subscript,
212         (objobjargproc) tdb_ass_subscript
213 };
214
215 /*
216  * Utility methods
217  */
218
219 /* Return non-zero if a given key exists in the tdb */
220
221 PyObject *py_tdb_hnd_has_key(PyObject *self, PyObject *args)
222 {
223         tdb_hnd_object *obj = (tdb_hnd_object *)self;
224         TDB_DATA key;
225
226         if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
227                 return NULL;
228
229         if (!obj->tdb) {
230                 PyErr_SetString(
231                         py_tdb_error, "tdb object has been closed"); 
232                 return NULL;
233         }       
234
235         return PyInt_FromLong(tdb_exists(obj->tdb, key));
236 }
237
238 /* Return a list of keys in the tdb */
239
240 static int tdb_traverse_keys(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
241                              void *state)
242 {
243         PyObject *key_list = (PyObject *)state;
244
245         PyList_Append(key_list, 
246                       PyString_FromStringAndSize(key.dptr, key.dsize));
247
248         return 0;
249 }
250
251 PyObject *py_tdb_hnd_keys(PyObject *self, PyObject *args)
252 {
253         tdb_hnd_object *obj = (tdb_hnd_object *)self;
254         PyObject *key_list = PyList_New(0);
255
256         if (!obj->tdb) {
257                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
258                 return NULL;
259         }       
260
261         if (tdb_traverse(obj->tdb, tdb_traverse_keys, key_list) == -1) {
262                 PyErr_SetString(py_tdb_error, "error traversing tdb");
263                 Py_DECREF(key_list);
264                 return NULL;
265         }
266
267         return key_list;        
268 }
269
270 PyObject *py_tdb_hnd_first_key(PyObject *self, PyObject *args)
271 {
272         tdb_hnd_object *obj = (tdb_hnd_object *)self;
273         TDB_DATA key;
274
275         if (!obj->tdb) {
276                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
277                 return NULL;
278         }       
279
280         key = tdb_firstkey(obj->tdb);
281
282         return Py_BuildValue("s#", key.dptr, key.dsize);
283 }
284
285 PyObject *py_tdb_hnd_next_key(PyObject *self, PyObject *py_oldkey)
286 {
287         tdb_hnd_object *obj = (tdb_hnd_object *)self;
288         TDB_DATA key, oldkey;
289
290         if (!obj->tdb) {
291                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
292                 return NULL;
293         }       
294
295         if (!PyArg_Parse(py_oldkey, "s#", &oldkey.dptr, &oldkey.dsize))
296                 return NULL;
297
298         key = tdb_nextkey(obj->tdb, oldkey);
299
300         return Py_BuildValue("s#", key.dptr, key.dsize);
301 }
302
303 /*
304  * Locking routines
305  */
306
307 PyObject *py_tdb_hnd_lock_all(PyObject *self, PyObject *args)
308 {
309         tdb_hnd_object *obj = (tdb_hnd_object *)self;
310         int result;
311
312         if (!obj->tdb) {
313                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
314                 return NULL;
315         }       
316
317         result = tdb_lockall(obj->tdb);
318
319         return PyInt_FromLong(result != -1);
320 }
321
322 PyObject *py_tdb_hnd_unlock_all(PyObject *self, PyObject *args)
323 {
324         tdb_hnd_object *obj = (tdb_hnd_object *)self;
325
326         if (!obj->tdb) {
327                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
328                 return NULL;
329         }       
330
331         tdb_unlockall(obj->tdb);
332
333         Py_INCREF(Py_None);
334         return Py_None;
335 }
336
337 /* Return an array of keys from a python object which must be a string or a
338    list of strings. */
339
340 static BOOL make_lock_list(PyObject *py_keys, TDB_DATA **keys, int *num_keys)
341 {
342         /* Are we a list or a string? */
343
344         if (!PyList_Check(py_keys) && !PyString_Check(py_keys)) {
345                 PyErr_SetString(PyExc_TypeError, "arg must be list of string");
346                 return False;
347         }
348
349         if (PyList_Check(py_keys)) {
350                 int i;
351
352                 /* Turn python list into array of keys */
353                 
354                 *num_keys = PyList_Size(py_keys);
355                 *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA) * (*num_keys));
356                 
357                 for (i = 0; i < *num_keys; i++) {
358                         PyObject *key = PyList_GetItem(py_keys, i);
359                         
360                         if (!PyString_Check(key)) {
361                                 PyErr_SetString(
362                                         PyExc_TypeError,
363                                         "list elements must be strings");
364                                 return False;
365                         }
366
367                         PyArg_Parse(key, "s#", &(*keys)[i].dptr, 
368                                     &(*keys)[i].dsize);
369                 }
370
371         } else {
372
373                 /* Turn python string into a single key */
374
375                 *keys = (TDB_DATA *)malloc(sizeof(TDB_DATA));
376                 *num_keys = 1;
377                 PyArg_Parse(py_keys, "s#", &(*keys)->dptr, &(*keys)->dsize);
378         }
379
380         return True;
381 }
382
383 PyObject *py_tdb_hnd_lock(PyObject *self, PyObject *args)
384 {
385         tdb_hnd_object *obj = (tdb_hnd_object *)self;
386         PyObject *py_keys;
387         TDB_DATA *keys;
388         int num_keys, result;
389
390         if (!obj->tdb) {
391                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
392                 return NULL;
393         }       
394
395         if (!PyArg_ParseTuple(args, "O", &py_keys))
396                 return NULL;
397
398         if (!make_lock_list(py_keys, &keys, &num_keys))
399                 return NULL;
400
401         result = tdb_lockkeys(obj->tdb, num_keys, keys);
402
403         free(keys);
404
405         return PyInt_FromLong(result != -1);
406 }
407
408 PyObject *py_tdb_hnd_unlock(PyObject *self, PyObject *args)
409 {
410         tdb_hnd_object *obj = (tdb_hnd_object *)self;
411         
412         if (!obj->tdb) {
413                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
414                 return NULL;
415         }       
416         
417         if (!PyArg_ParseTuple(args, ""))
418                 return NULL;
419         
420         tdb_unlockkeys(obj->tdb);
421
422         Py_INCREF(Py_None);
423         return Py_None;
424 }
425
426 /*
427  * tdb traversal
428  */
429
430 struct traverse_info {
431         PyObject *callback;
432         PyObject *state;
433 };
434
435 static int tdb_traverse_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA value,
436                                  void *state)
437 {
438         struct traverse_info *info = state;
439         PyObject *arglist, *py_result;
440         int result;
441
442         arglist = Py_BuildValue("(s#s#O)", key.dptr, key.dsize, value.dptr,
443                                 value.dsize, info->state);
444
445         py_result = PyEval_CallObject(info->callback, arglist);
446
447         Py_DECREF(arglist);
448         
449         if (!PyInt_Check(py_result)) {
450                 result = 1;     /* Hmm - non-integer object returned by callback */
451                 goto done;
452         }
453
454         result = PyInt_AsLong(py_result);
455
456 done:
457         Py_DECREF(py_result);
458         return result;
459 }
460
461 PyObject *py_tdb_hnd_traverse(PyObject *self, PyObject *args, PyObject *kw)
462 {
463         tdb_hnd_object *obj = (tdb_hnd_object *)self;
464         static char *kwlist[] = { "traverse_fn", "state", NULL };
465         PyObject *state = Py_None, *callback;
466         struct traverse_info info;
467         int result;
468
469         if (!PyArg_ParseTupleAndKeywords(
470                     args, kw, "O|O", kwlist, &callback, &state))
471                 return NULL;
472
473         if (!PyCallable_Check(callback)) {
474                 PyErr_SetString(PyExc_TypeError, "parameter must be callable");
475                 return NULL;
476         }
477
478         Py_INCREF(callback);
479         Py_INCREF(state);
480
481         info.callback = callback;
482         info.state = state;
483
484         result = tdb_traverse(obj->tdb, tdb_traverse_traverse, &info);
485
486         Py_DECREF(callback);
487         Py_DECREF(state);
488
489         return PyInt_FromLong(result);
490 }
491
492 PyObject *py_tdb_hnd_chainlock(PyObject *self, PyObject *args)
493 {
494         tdb_hnd_object *obj = (tdb_hnd_object *)self;
495         TDB_DATA key;
496         int result;
497
498         if (!obj->tdb) {
499                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
500                 return NULL;
501         }       
502
503         if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
504                 return NULL;
505
506         result = tdb_chainlock(obj->tdb, key);
507
508         return PyInt_FromLong(result != -1);
509 }
510
511 PyObject *py_tdb_hnd_chainunlock(PyObject *self, PyObject *args)
512 {
513         tdb_hnd_object *obj = (tdb_hnd_object *)self;
514         TDB_DATA key;
515         int result;
516
517         if (!obj->tdb) {
518                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
519                 return NULL;
520         }       
521
522         if (!PyArg_ParseTuple(args, "s#", &key.dptr, &key.dsize))
523                 return NULL;
524
525         result = tdb_chainunlock(obj->tdb, key);
526
527         return PyInt_FromLong(result != -1);
528 }
529
530 PyObject *py_tdb_hnd_lock_bystring(PyObject *self, PyObject *args)
531 {
532         tdb_hnd_object *obj = (tdb_hnd_object *)self;
533         int result, timeout = 30;
534         char *s;
535
536         if (!obj->tdb) {
537                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
538                 return NULL;
539         }       
540
541         if (!PyArg_ParseTuple(args, "s|i", &s, &timeout))
542                 return NULL;
543
544         result = tdb_lock_bystring(obj->tdb, s, timeout);
545
546         return PyInt_FromLong(result != -1);
547 }
548
549 PyObject *py_tdb_hnd_unlock_bystring(PyObject *self, PyObject *args)
550 {
551         tdb_hnd_object *obj = (tdb_hnd_object *)self;
552         char *s;
553
554         if (!obj->tdb) {
555                 PyErr_SetString(py_tdb_error, "tdb object has been closed"); 
556                 return NULL;
557         }       
558
559         if (!PyArg_ParseTuple(args, "s", &s))
560                 return NULL;
561
562         tdb_unlock_bystring(obj->tdb, s);
563
564         Py_INCREF(Py_None);
565         return Py_None;
566 }
567
568 /* 
569  * Method dispatch table for this module
570  */
571
572 static PyMethodDef tdb_methods[] = {
573         { "open", (PyCFunction)py_tdb_open, METH_VARARGS | METH_KEYWORDS },
574         { "close", (PyCFunction)py_tdb_close, METH_VARARGS },
575         { NULL }
576 };
577
578 /* 
579  * Methods on a tdb object
580  */
581
582 static PyMethodDef tdb_hnd_methods[] = {
583         { "keys", (PyCFunction)py_tdb_hnd_keys, METH_VARARGS },
584         { "has_key", (PyCFunction)py_tdb_hnd_has_key, METH_VARARGS },
585         { "first_key", (PyCFunction)py_tdb_hnd_first_key, METH_VARARGS },
586         { "next_key", (PyCFunction)py_tdb_hnd_next_key, METH_VARARGS },
587         { "lock_all", (PyCFunction)py_tdb_hnd_lock_all, METH_VARARGS },
588         { "unlock_all", (PyCFunction)py_tdb_hnd_unlock_all, METH_VARARGS },
589         { "lock", (PyCFunction)py_tdb_hnd_lock, METH_VARARGS },
590         { "unlock", (PyCFunction)py_tdb_hnd_unlock, METH_VARARGS },
591         { "traverse", (PyCFunction)py_tdb_hnd_traverse, METH_VARARGS | METH_KEYWORDS },
592         { "chainlock", (PyCFunction)py_tdb_hnd_chainlock, METH_VARARGS | METH_KEYWORDS },
593         { "chainunlock", (PyCFunction)py_tdb_hnd_chainunlock, METH_VARARGS | METH_KEYWORDS },
594         { "lock_bystring", (PyCFunction)py_tdb_hnd_lock_bystring, METH_VARARGS | METH_KEYWORDS },
595         { "unlock_bystring", (PyCFunction)py_tdb_hnd_unlock_bystring, METH_VARARGS | METH_KEYWORDS },
596         { NULL }
597 };
598
599 /* Deallocate a tdb handle object */
600
601 static void tdb_hnd_dealloc(PyObject* self)
602 {
603         tdb_hnd_object *hnd = (tdb_hnd_object *)self;
604
605         if (hnd->tdb) {
606                 tdb_close(hnd->tdb);
607                 hnd->tdb = NULL;
608         }
609 }
610
611 /* Return tdb handle attributes */
612
613 static PyObject *tdb_hnd_getattr(PyObject *self, char *attrname)
614 {
615         return Py_FindMethod(tdb_hnd_methods, self, attrname);
616 }
617
618 static char tdb_hnd_type_doc[] = 
619 "Python wrapper for tdb.";
620
621 PyTypeObject tdb_hnd_type = {
622         PyObject_HEAD_INIT(NULL)
623         0,
624         "tdb",
625         sizeof(tdb_hnd_object),
626         0,
627         tdb_hnd_dealloc,        /* tp_dealloc*/
628         0,                      /* tp_print*/
629         tdb_hnd_getattr,        /* tp_getattr*/
630         0,                      /* tp_setattr*/
631         0,                      /* tp_compare*/
632         0,                      /* tp_repr*/
633         0,                      /* tp_as_number*/
634         0,                      /* tp_as_sequence*/
635         &tdb_mapping,           /* tp_as_mapping*/
636         0,                      /* tp_hash */
637         0,                      /* tp_call */
638         0,                      /* tp_str */
639         0,                      /* tp_getattro */
640         0,                      /* tp_setattro */
641         0,                      /* tp_as_buffer*/
642         Py_TPFLAGS_DEFAULT,     /* tp_flags */
643         tdb_hnd_type_doc,       /* tp_doc */
644 };
645
646 /* Constants */
647
648 static struct const_vals {
649         char *name;
650         uint32 value;
651 } module_const_vals[] = {
652
653         /* Flags for tdb_open() */
654
655         { "TDB_DEFAULT", TDB_DEFAULT },
656         { "TDB_CLEAR_IF_FIRST", TDB_CLEAR_IF_FIRST },
657         { "TDB_INTERNAL", TDB_INTERNAL },
658         { "TDB_NOLOCK", TDB_NOLOCK },
659         { "TDB_NOMMAP", TDB_NOMMAP },
660         { "TDB_CONVERT", TDB_CONVERT },
661         { "TDB_BIGENDIAN", TDB_BIGENDIAN },
662         
663         { NULL },
664 };
665
666 static void const_init(PyObject *dict)
667 {
668         struct const_vals *tmp;
669         PyObject *obj;
670
671         for (tmp = module_const_vals; tmp->name; tmp++) {
672                 obj = PyInt_FromLong(tmp->value);
673                 PyDict_SetItemString(dict, tmp->name, obj);
674                 Py_DECREF(obj);
675         }
676 }
677
678 /* Module initialisation */
679
680 void inittdb(void)
681 {
682         PyObject *module, *dict;
683
684         /* Initialise module */
685
686         module = Py_InitModule("tdb", tdb_methods);
687         dict = PyModule_GetDict(module);
688
689         py_tdb_error = PyErr_NewException("tdb.error", NULL, NULL);
690         PyDict_SetItemString(dict, "error", py_tdb_error);
691
692         /* Initialise policy handle object */
693
694         tdb_hnd_type.ob_type = &PyType_Type;
695
696         PyDict_SetItemString(dict, "tdb.hnd", 
697                              (PyObject *)&tdb_hnd_type);
698
699         /* Initialise constants */
700
701         const_init(dict);
702 }