]> git.k1024.org Git - debian-pylibacl.git/blob - acl.c
Imported Upstream version 0.4.0
[debian-pylibacl.git] / acl.c
1 /*
2     posix1e - a python module exposing the posix acl functions
3
4     Copyright (C) 2002-2008 Iustin Pop <iusty@k1024.org>
5
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2.1 of the License, or (at your option) any later version.
10
11     This library 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 GNU
14     Lesser General Public License for more details.
15
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19     02110-1301  USA
20
21 */
22
23 #include <Python.h>
24
25 #include <sys/types.h>
26 #include <sys/acl.h>
27
28 #ifdef HAVE_LINUX
29 #include <acl/libacl.h>
30 #define get_perm acl_get_perm
31 #elif HAVE_FREEBSD
32 #define get_perm acl_get_perm_np
33 #endif
34
35 staticforward PyTypeObject ACL_Type;
36 static PyObject* ACL_applyto(PyObject* obj, PyObject* args);
37 static PyObject* ACL_valid(PyObject* obj, PyObject* args);
38
39 #ifdef HAVE_ACL_COPY_EXT
40 static PyObject* ACL_get_state(PyObject *obj, PyObject* args);
41 static PyObject* ACL_set_state(PyObject *obj, PyObject* args);
42 #endif
43
44 #ifdef HAVE_LEVEL2
45 staticforward PyTypeObject Entry_Type;
46 staticforward PyTypeObject Permset_Type;
47 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
48                              PyObject *keywds);
49 #endif
50
51 static acl_perm_t holder_ACL_EXECUTE = ACL_EXECUTE;
52 static acl_perm_t holder_ACL_READ = ACL_READ;
53 static acl_perm_t holder_ACL_WRITE = ACL_WRITE;
54
55 typedef struct {
56     PyObject_HEAD
57     acl_t acl;
58 #ifdef HAVE_LEVEL2
59     int entry_id;
60 #endif
61 } ACL_Object;
62
63 #ifdef HAVE_LEVEL2
64
65 typedef struct {
66     PyObject_HEAD
67     PyObject *parent_acl; /* The parent acl, so it won't run out on us */
68     acl_entry_t entry;
69 } Entry_Object;
70
71 typedef struct {
72     PyObject_HEAD
73     PyObject *parent_entry; /* The parent entry, so it won't run out on us */
74     acl_permset_t permset;
75 } Permset_Object;
76
77 #endif
78
79 /* Creation of a new ACL instance */
80 static PyObject* ACL_new(PyTypeObject* type, PyObject* args,
81                          PyObject *keywds) {
82     PyObject* newacl;
83
84     newacl = type->tp_alloc(type, 0);
85
86     if(newacl != NULL) {
87         ((ACL_Object*)newacl)->acl = NULL;
88 #ifdef HAVEL_LEVEL2
89         ((ACL_Object*)newacl)->entry_id = ACL_FIRST_ENTRY;
90 #endif
91     }
92
93     return newacl;
94 }
95
96 /* Initialization of a new ACL instance */
97 static int ACL_init(PyObject* obj, PyObject* args, PyObject *keywds) {
98     ACL_Object* self = (ACL_Object*) obj;
99 #ifdef HAVE_LINUX
100     static char *kwlist[] = { "file", "fd", "text", "acl", "filedef",
101                               "mode", NULL };
102     char *format = "|sisO!sH";
103     mode_t mode = 0;
104 #else
105     static char *kwlist[] = { "file", "fd", "text", "acl", "filedef", NULL };
106     char *format = "|sisO!s";
107 #endif
108     char *file = NULL;
109     char *filedef = NULL;
110     char *text = NULL;
111     int fd = -1;
112     ACL_Object* thesrc = NULL;
113
114     if(!PyTuple_Check(args) || PyTuple_Size(args) != 0 ||
115        (keywds != NULL && PyDict_Check(keywds) && PyDict_Size(keywds) > 1)) {
116         PyErr_SetString(PyExc_ValueError, "a max of one keyword argument"
117                         " must be passed");
118         return -1;
119     }
120     if(!PyArg_ParseTupleAndKeywords(args, keywds, format, kwlist,
121                                     &file, &fd, &text, &ACL_Type,
122                                     &thesrc, &filedef
123 #ifdef HAVE_LINUX
124                                     , &mode
125 #endif
126                                     ))
127         return -1;
128
129     /* Free the old acl_t without checking for error, we don't
130      * care right now */
131     if(self->acl != NULL)
132         acl_free(self->acl);
133
134     if(file != NULL)
135         self->acl = acl_get_file(file, ACL_TYPE_ACCESS);
136     else if(text != NULL)
137         self->acl = acl_from_text(text);
138     else if(fd != -1)
139         self->acl = acl_get_fd(fd);
140     else if(thesrc != NULL)
141         self->acl = acl_dup(thesrc->acl);
142     else if(filedef != NULL)
143         self->acl = acl_get_file(filedef, ACL_TYPE_DEFAULT);
144 #ifdef HAVE_LINUX
145     else if(PyMapping_HasKeyString(keywds, kwlist[5]))
146         self->acl = acl_from_mode(mode);
147 #endif
148     else
149         self->acl = acl_init(0);
150
151     if(self->acl == NULL) {
152         PyErr_SetFromErrno(PyExc_IOError);
153         return -1;
154     }
155
156     return 0;
157 }
158
159 /* Standard type functions */
160 static void ACL_dealloc(PyObject* obj) {
161     ACL_Object *self = (ACL_Object*) obj;
162     PyObject *err_type, *err_value, *err_traceback;
163     int have_error = PyErr_Occurred() ? 1 : 0;
164
165     if (have_error)
166         PyErr_Fetch(&err_type, &err_value, &err_traceback);
167     if(self->acl != NULL && acl_free(self->acl) != 0)
168         PyErr_WriteUnraisable(obj);
169     if (have_error)
170         PyErr_Restore(err_type, err_value, err_traceback);
171     PyObject_DEL(self);
172 }
173
174 /* Converts the acl to a text format */
175 static PyObject* ACL_str(PyObject *obj) {
176     char *text;
177     ACL_Object *self = (ACL_Object*) obj;
178     PyObject *ret;
179
180     text = acl_to_text(self->acl, NULL);
181     if(text == NULL) {
182         return PyErr_SetFromErrno(PyExc_IOError);
183     }
184     ret = PyString_FromString(text);
185     if(acl_free(text) != 0) {
186         Py_DECREF(ret);
187         return PyErr_SetFromErrno(PyExc_IOError);
188     }
189     return ret;
190 }
191
192 #ifdef HAVE_LINUX
193 static char __to_any_text_doc__[] =
194   "Convert the ACL to a custom text format.\n"
195   "\n"
196   "This method encapsulates the acl_to_any_text function. It allows a \n"
197   "customized text format to be generated for the ACL. See\n"
198   "acl_to_any_text(3) for more details.\n"
199   "\n"
200   "Parameters:\n"
201   "  - prefix: if given, this string will be prepended to all lines\n"
202   "  - separator: a single character (defaults to '\\n'); this will be\n"
203   "               user to separate the entries in the ACL\n"
204   "  - options: a bitwise combination of:\n"
205   "    -  TEXT_ABBREVIATE: use 'u' instead of 'user', 'g' instead of \n"
206   "                       'group', etc.\n"
207   "    -  TEXT_NUMERIC_IDS: User and group IDs are included as decimal\n"
208   "                         numbers instead of names\n"
209   "    -  TEXT_SOME_EFFECTIVE: Include comments denoting the effective\n"
210   "                            permissions when some are masked\n"
211   "    -  TEXT_ALL_EFFECTIVE: Include comments after all ACL entries\n"
212   "                           affected by an ACL_MASK entry\n"
213   "    -  TEXT_SMART_INDENT: Used in combination with the _EFFECTIVE\n"
214   "                          options, this will ensure that comments \n"
215   "                          are alligned to the fourth tab position\n"
216   "                          (assuming one tab equals eight spaces)\n"
217   ;
218
219 /* Converts the acl to a custom text format */
220 static PyObject* ACL_to_any_text(PyObject *obj, PyObject *args,
221                                  PyObject *kwds) {
222     char *text;
223     ACL_Object *self = (ACL_Object*) obj;
224     PyObject *ret;
225     char *arg_prefix = NULL;
226     char arg_separator = '\n';
227     int arg_options = 0;
228     static char *kwlist[] = {"prefix", "separator", "options", NULL};
229
230     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sci", kwlist, &arg_prefix,
231                                      &arg_separator, &arg_options))
232       return NULL;
233
234     text = acl_to_any_text(self->acl, arg_prefix, arg_separator, arg_options);
235     if(text == NULL) {
236         return PyErr_SetFromErrno(PyExc_IOError);
237     }
238     ret = PyString_FromString(text);
239     if(acl_free(text) != 0) {
240         Py_DECREF(ret);
241         return PyErr_SetFromErrno(PyExc_IOError);
242     }
243     return ret;
244 }
245
246 static char __check_doc__[] =
247     "Check the ACL validity.\n"
248     "\n"
249     "This is a non-portable, Linux specific extension that allow more\n"
250     "information to be retrieved in case an ACL is not valid than the\n"
251     "validate() method.\n"
252     "\n"
253     "This method will return either False (the ACL is valid), or a tuple\n"
254     "with two elements. The first element is one of the following\n"
255     "constants:\n"
256     "  - ACL_MULTI_ERROR: The ACL contains multiple entries that have a\n"
257     "                     tag type that may occur at most once\n"
258     "  - ACL_DUPLICATE_ERROR: The ACL contains multiple ACL_USER or \n"
259     "                         ACL_GROUP entries  with the same ID\n"
260     "  - ACL_MISS_ERROR: A required entry is missing\n"
261     "  - ACL_ENTRY_ERROR: The ACL contains an invalid entry tag type\n"
262     "\n"
263     "The second element of the tuple is the index of the entry that is\n"
264     "invalid (in the same order as by iterating over the ACL entry)\n"
265     ;
266
267 /* The acl_check method */
268 static PyObject* ACL_check(PyObject* obj, PyObject* args) {
269     ACL_Object *self = (ACL_Object*) obj;
270     int result;
271     int eindex;
272
273     if((result = acl_check(self->acl, &eindex)) == -1)
274         return PyErr_SetFromErrno(PyExc_IOError);
275     if(result == 0) {
276         Py_INCREF(Py_False);
277         return Py_False;
278     }
279     return PyTuple_Pack(2, PyInt_FromLong(result), PyInt_FromLong(eindex));
280 }
281
282 /* Implementation of the rich compare for ACLs */
283 static PyObject* ACL_richcompare(PyObject* o1, PyObject* o2, int op) {
284     ACL_Object *acl1, *acl2;
285     int n;
286     PyObject *ret;
287
288     if(!PyObject_IsInstance(o2, (PyObject*)&ACL_Type)) {
289         if(op == Py_EQ)
290             Py_RETURN_FALSE;
291         if(op == Py_NE)
292             Py_RETURN_TRUE;
293         PyErr_SetString(PyExc_TypeError, "can only compare to an ACL");
294         return NULL;
295     }
296
297     acl1 = (ACL_Object*)o1;
298     acl2 = (ACL_Object*)o2;
299     if((n=acl_cmp(acl1->acl, acl2->acl))==-1)
300         return PyErr_SetFromErrno(PyExc_IOError);
301     switch(op) {
302     case Py_EQ:
303         ret = n == 0 ? Py_True : Py_False;
304         break;
305     case Py_NE:
306         ret = n == 1 ? Py_True : Py_False;
307         break;
308     default:
309         ret = Py_NotImplemented;
310     }
311     Py_INCREF(ret);
312     return ret;
313 }
314
315 static char __equiv_mode_doc__[] =
316     "Return the octal mode the ACL is equivalent to.\n"
317     "\n"
318     "This is a non-portable, Linux specific extension that checks\n"
319     "if the ACL is a basic ACL and returns the corresponding mode.\n"
320     "\n"
321     "An IOerror exception will be raised if the ACL is an extended ACL\n"
322     ;
323
324 /* The acl_equiv_mode method */
325 static PyObject* ACL_equiv_mode(PyObject* obj, PyObject* args) {
326     ACL_Object *self = (ACL_Object*) obj;
327     mode_t mode;
328
329     if(acl_equiv_mode(self->acl, &mode) == -1)
330         return PyErr_SetFromErrno(PyExc_IOError);
331     return PyInt_FromLong(mode);
332 }
333 #endif
334
335 /* Implementation of the compare for ACLs */
336 static int ACL_nocmp(PyObject* o1, PyObject* o2) {
337
338     PyErr_SetString(PyExc_TypeError, "cannot compare ACLs using cmp()");
339     return -1;
340 }
341
342 /* Custom methods */
343 static char __applyto_doc__[] =
344     "Apply the ACL to a file or filehandle.\n"
345     "\n"
346     "Parameters:\n"
347     "  - either a filename or a file-like object or an integer; this\n"
348     "    represents the filesystem object on which to act\n"
349     "  - optional flag representing the type of ACL to set, either\n"
350     "    ACL_TYPE_ACCESS (default) or ACL_TYPE_DEFAULT\n"
351     ;
352
353 /* Applyes the ACL to a file */
354 static PyObject* ACL_applyto(PyObject* obj, PyObject* args) {
355     ACL_Object *self = (ACL_Object*) obj;
356     PyObject *myarg;
357     acl_type_t type = ACL_TYPE_ACCESS;
358     int nret;
359     int fd;
360
361     if (!PyArg_ParseTuple(args, "O|i", &myarg, &type))
362         return NULL;
363
364     if(PyString_Check(myarg)) {
365         char *filename = PyString_AS_STRING(myarg);
366         nret = acl_set_file(filename, type, self->acl);
367     } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
368         nret = acl_set_fd(fd, self->acl);
369     } else {
370         PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int,"
371                         " or file-like object");
372         return 0;
373     }
374     if(nret == -1) {
375         return PyErr_SetFromErrno(PyExc_IOError);
376     }
377
378     /* Return the result */
379     Py_INCREF(Py_None);
380     return Py_None;
381 }
382
383 static char __valid_doc__[] =
384     "Test the ACL for validity.\n"
385     "\n"
386     "This method tests the ACL to see if it is a valid ACL\n"
387     "in terms of the filesystem. More precisely, it checks that:\n"
388     "\n"
389     "The ACL contains exactly one entry with each of the\n"
390     "ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER tag types. Entries\n"
391     "with ACL_USER and ACL_GROUP tag types may appear zero or more\n"
392     "times in an ACL. An ACL that contains entries of ACL_USER or\n"
393     "ACL_GROUP tag types must contain exactly one entry of the \n"
394     "ACL_MASK tag type. If an ACL contains no entries of\n"
395     "ACL_USER or ACL_GROUP tag types, the ACL_MASK entry is optional.\n"
396     "\n"
397     "All user ID qualifiers must be unique among all entries of\n"
398     "the ACL_USER tag type, and all group IDs must be unique among all\n"
399     "entries of ACL_GROUP tag type.\n"
400     "\n"
401     "The method will return 1 for a valid ACL and 0 for an invalid one.\n"
402     "This has been chosen because the specification for acl_valid in\n"
403     "the POSIX.1e standard documents only one possible value for errno\n"
404     "in case of an invalid ACL, so we can't differentiate between\n"
405     "classes of errors. Other suggestions are welcome.\n"
406     ;
407
408 /* Checks the ACL for validity */
409 static PyObject* ACL_valid(PyObject* obj, PyObject* args) {
410     ACL_Object *self = (ACL_Object*) obj;
411
412     if(acl_valid(self->acl) == -1) {
413         Py_INCREF(Py_False);
414         return Py_False;
415     } else {
416         Py_INCREF(Py_True);
417         return Py_True;
418     }
419 }
420
421 #ifdef HAVE_ACL_COPY_EXT
422 static PyObject* ACL_get_state(PyObject *obj, PyObject* args) {
423     ACL_Object *self = (ACL_Object*) obj;
424     PyObject *ret;
425     ssize_t size, nsize;
426     char *buf;
427
428     size = acl_size(self->acl);
429     if(size == -1)
430         return PyErr_SetFromErrno(PyExc_IOError);
431
432     if((ret = PyString_FromStringAndSize(NULL, size)) == NULL)
433         return NULL;
434     buf = PyString_AsString(ret);
435
436     if((nsize = acl_copy_ext(buf, self->acl, size)) == -1) {
437         Py_DECREF(ret);
438         return PyErr_SetFromErrno(PyExc_IOError);
439     }
440
441     return ret;
442 }
443
444 static PyObject* ACL_set_state(PyObject *obj, PyObject* args) {
445     ACL_Object *self = (ACL_Object*) obj;
446     const void *buf;
447     int bufsize;
448     acl_t ptr;
449
450     /* Parse the argument */
451     if (!PyArg_ParseTuple(args, "s#", &buf, &bufsize))
452         return NULL;
453
454     /* Try to import the external representation */
455     if((ptr = acl_copy_int(buf)) == NULL)
456         return PyErr_SetFromErrno(PyExc_IOError);
457
458     /* Free the old acl. Should we ignore errors here? */
459     if(self->acl != NULL) {
460         if(acl_free(self->acl) == -1)
461             return PyErr_SetFromErrno(PyExc_IOError);
462     }
463
464     self->acl = ptr;
465
466     /* Return the result */
467     Py_INCREF(Py_None);
468     return Py_None;
469 }
470 #endif
471
472 #ifdef HAVE_LEVEL2
473
474 /* tp_iter for the ACL type; since it can be iterated only
475  * destructively, the type is its iterator
476  */
477 static PyObject* ACL_iter(PyObject *obj) {
478     ACL_Object *self = (ACL_Object*)obj;
479     self->entry_id = ACL_FIRST_ENTRY;
480     Py_INCREF(obj);
481     return obj;
482 }
483
484 /* the tp_iternext function for the ACL type */
485 static PyObject* ACL_iternext(PyObject *obj) {
486     ACL_Object *self = (ACL_Object*)obj;
487     acl_entry_t the_entry_t;
488     Entry_Object *the_entry_obj;
489     int nerr;
490
491     nerr = acl_get_entry(self->acl, self->entry_id, &the_entry_t);
492     self->entry_id = ACL_NEXT_ENTRY;
493     if(nerr == -1)
494         return PyErr_SetFromErrno(PyExc_IOError);
495     else if(nerr == 0) {
496         /* Docs says this is not needed */
497         /*PyErr_SetObject(PyExc_StopIteration, Py_None);*/
498         return NULL;
499     }
500
501     the_entry_obj = (Entry_Object*) PyType_GenericNew(&Entry_Type, NULL, NULL);
502     if(the_entry_obj == NULL)
503         return NULL;
504
505     the_entry_obj->entry = the_entry_t;
506
507     the_entry_obj->parent_acl = obj;
508     Py_INCREF(obj); /* For the reference we have in entry->parent */
509
510     return (PyObject*)the_entry_obj;
511 }
512
513 static char __ACL_delete_entry_doc__[] =
514     "Deletes an entry from the ACL.\n"
515     "\n"
516     "Note: Only with level 2\n"
517     "Parameters:\n"
518     "  - the Entry object which should be deleted; note that after\n"
519     "    this function is called, that object is unusable any longer\n"
520     "    and should be deleted\n"
521     ;
522
523 /* Deletes an entry from the ACL */
524 static PyObject* ACL_delete_entry(PyObject *obj, PyObject *args) {
525     ACL_Object *self = (ACL_Object*)obj;
526     Entry_Object *e;
527
528     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &e))
529         return NULL;
530
531     if(acl_delete_entry(self->acl, e->entry) == -1)
532         return PyErr_SetFromErrno(PyExc_IOError);
533
534     /* Return the result */
535     Py_INCREF(Py_None);
536     return Py_None;
537 }
538
539 static char __ACL_calc_mask_doc__[] =
540     "Compute the file group class mask.\n"
541     "\n"
542     "The calc_mask() method calculates and sets the permissions \n"
543     "associated with the ACL_MASK Entry of the ACL.\n"
544     "The value of the new permissions is the union of the permissions \n"
545     "granted by all entries of tag type ACL_GROUP, ACL_GROUP_OBJ, or \n"
546     "ACL_USER.  If the ACL already contains an ACL_MASK entry, its \n"
547     "permissions are overwritten; if it does not contain an ACL_MASK \n"
548     "Entry, one is added.\n"
549     "\n"
550     "The order of existing entries in the ACL is undefined after this \n"
551     "function.\n"
552     ;
553
554 /* Updates the mask entry in the ACL */
555 static PyObject* ACL_calc_mask(PyObject *obj, PyObject *args) {
556     ACL_Object *self = (ACL_Object*)obj;
557
558     if(acl_calc_mask(&self->acl) == -1)
559         return PyErr_SetFromErrno(PyExc_IOError);
560
561     /* Return the result */
562     Py_INCREF(Py_None);
563     return Py_None;
564 }
565
566 static char __ACL_append_doc__[] =
567     "Append a new Entry to the ACL and return it.\n"
568     "\n"
569     "This is a convenience function to create a new Entry \n"
570     "and append it to the ACL.\n"
571     "If a parameter of type Entry instance is given, the \n"
572     "entry will be a copy of that one (as if copied with \n"
573     "Entry.copy()), otherwise, the new entry will be empty.\n"
574     ;
575
576 /* Convenience method to create a new Entry */
577 static PyObject* ACL_append(PyObject *obj, PyObject *args) {
578     ACL_Object* self = (ACL_Object*) obj;
579     Entry_Object* newentry;
580     Entry_Object* oldentry = NULL;
581     int nret;
582
583     newentry = (Entry_Object*)PyType_GenericNew(&Entry_Type, NULL, NULL);
584     if(newentry == NULL) {
585         return NULL;
586     }
587
588     if (!PyArg_ParseTuple(args, "|O!", &Entry_Type, &oldentry))
589         return NULL;
590
591     nret = acl_create_entry(&self->acl, &newentry->entry);
592     if(nret == -1) {
593         Py_DECREF(newentry);
594         return PyErr_SetFromErrno(PyExc_IOError);
595     }
596
597     if(oldentry != NULL) {
598         nret = acl_copy_entry(newentry->entry, oldentry->entry);
599         if(nret == -1) {
600             Py_DECREF(newentry);
601             return PyErr_SetFromErrno(PyExc_IOError);
602         }
603     }
604
605     newentry->parent_acl = obj;
606     Py_INCREF(obj);
607
608     return (PyObject*)newentry;
609 }
610
611 /***** Entry type *****/
612
613 /* Creation of a new Entry instance */
614 static PyObject* Entry_new(PyTypeObject* type, PyObject* args,
615                            PyObject *keywds) {
616     PyObject* newentry;
617
618     newentry = PyType_GenericNew(type, args, keywds);
619
620     if(newentry != NULL) {
621         ((Entry_Object*)newentry)->entry = NULL;
622         ((Entry_Object*)newentry)->parent_acl = NULL;
623     }
624
625     return newentry;
626 }
627
628 /* Initialization of a new Entry instance */
629 static int Entry_init(PyObject* obj, PyObject* args, PyObject *keywds) {
630     Entry_Object* self = (Entry_Object*) obj;
631     ACL_Object* parent = NULL;
632
633     if (!PyArg_ParseTuple(args, "O!", &ACL_Type, &parent))
634         return -1;
635
636     if(acl_create_entry(&parent->acl, &self->entry) == -1) {
637         PyErr_SetFromErrno(PyExc_IOError);
638         return -1;
639     }
640
641     self->parent_acl = (PyObject*)parent;
642     Py_INCREF(parent);
643
644     return 0;
645 }
646
647 /* Free the Entry instance */
648 static void Entry_dealloc(PyObject* obj) {
649     Entry_Object *self = (Entry_Object*) obj;
650     PyObject *err_type, *err_value, *err_traceback;
651     int have_error = PyErr_Occurred() ? 1 : 0;
652
653     if (have_error)
654         PyErr_Fetch(&err_type, &err_value, &err_traceback);
655     if(self->parent_acl != NULL) {
656         Py_DECREF(self->parent_acl);
657         self->parent_acl = NULL;
658     }
659     if (have_error)
660         PyErr_Restore(err_type, err_value, err_traceback);
661     PyObject_DEL(self);
662 }
663
664 /* Converts the entry to a text format */
665 static PyObject* Entry_str(PyObject *obj) {
666     acl_tag_t tag;
667     uid_t qualifier;
668     void *p;
669     PyObject *ret;
670     PyObject *format, *list;
671     Entry_Object *self = (Entry_Object*) obj;
672
673     if(acl_get_tag_type(self->entry, &tag) == -1) {
674         PyErr_SetFromErrno(PyExc_IOError);
675         return NULL;
676     }
677     if(tag == ACL_USER || tag == ACL_GROUP) {
678         if((p = acl_get_qualifier(self->entry)) == NULL) {
679             PyErr_SetFromErrno(PyExc_IOError);
680             return NULL;
681         }
682         qualifier = *(uid_t*)p;
683         acl_free(p);
684     } else {
685         qualifier = 0;
686     }
687
688     format = PyString_FromString("ACL entry for %s");
689     if(format == NULL)
690         return NULL;
691     list = PyTuple_New(1);
692     if(tag == ACL_UNDEFINED_TAG) {
693         PyTuple_SetItem(list, 0, PyString_FromString("undefined type"));
694     } else if(tag == ACL_USER_OBJ) {
695         PyTuple_SetItem(list, 0, PyString_FromString("the owner"));
696     } else if(tag == ACL_GROUP_OBJ) {
697         PyTuple_SetItem(list, 0, PyString_FromString("the group"));
698     } else if(tag == ACL_OTHER) {
699         PyTuple_SetItem(list, 0, PyString_FromString("the others"));
700     } else if(tag == ACL_USER) {
701         PyTuple_SetItem(list, 0, PyString_FromFormat("user with uid %d",
702                                                      qualifier));
703     } else if(tag == ACL_GROUP) {
704         PyTuple_SetItem(list, 0, PyString_FromFormat("group with gid %d",
705                                                      qualifier));
706     } else if(tag == ACL_MASK) {
707         PyTuple_SetItem(list, 0, PyString_FromString("the mask"));
708     } else {
709         PyTuple_SetItem(list, 0, PyString_FromString("UNKNOWN_TAG_TYPE!"));
710     }
711     ret = PyString_Format(format, list);
712     Py_DECREF(format);
713     Py_DECREF(list);
714     return ret;
715 }
716
717 /* Sets the tag type of the entry */
718 static int Entry_set_tag_type(PyObject* obj, PyObject* value, void* arg) {
719     Entry_Object *self = (Entry_Object*) obj;
720
721     if(value == NULL) {
722         PyErr_SetString(PyExc_TypeError,
723                         "tag type deletion is not supported");
724         return -1;
725     }
726
727     if(!PyInt_Check(value)) {
728         PyErr_SetString(PyExc_TypeError,
729                         "tag type must be integer");
730         return -1;
731     }
732     if(acl_set_tag_type(self->entry, (acl_tag_t)PyInt_AsLong(value)) == -1) {
733         PyErr_SetFromErrno(PyExc_IOError);
734         return -1;
735     }
736
737     return 0;
738 }
739
740 /* Returns the tag type of the entry */
741 static PyObject* Entry_get_tag_type(PyObject *obj, void* arg) {
742     Entry_Object *self = (Entry_Object*) obj;
743     acl_tag_t value;
744
745     if (self->entry == NULL) {
746         PyErr_SetString(PyExc_AttributeError, "entry attribute");
747         return NULL;
748     }
749     if(acl_get_tag_type(self->entry, &value) == -1) {
750         PyErr_SetFromErrno(PyExc_IOError);
751         return NULL;
752     }
753
754     return PyInt_FromLong(value);
755 }
756
757 /* Sets the qualifier (either uid_t or gid_t) for the entry,
758  * usable only if the tag type if ACL_USER or ACL_GROUP
759  */
760 static int Entry_set_qualifier(PyObject* obj, PyObject* value, void* arg) {
761     Entry_Object *self = (Entry_Object*) obj;
762     int uidgid;
763
764     if(value == NULL) {
765         PyErr_SetString(PyExc_TypeError,
766                         "qualifier deletion is not supported");
767         return -1;
768     }
769
770     if(!PyInt_Check(value)) {
771         PyErr_SetString(PyExc_TypeError,
772                         "tag type must be integer");
773         return -1;
774     }
775     uidgid = PyInt_AsLong(value);
776     if(acl_set_qualifier(self->entry, (void*)&uidgid) == -1) {
777         PyErr_SetFromErrno(PyExc_IOError);
778         return -1;
779     }
780
781     return 0;
782 }
783
784 /* Returns the qualifier of the entry */
785 static PyObject* Entry_get_qualifier(PyObject *obj, void* arg) {
786     Entry_Object *self = (Entry_Object*) obj;
787     void *p;
788     int value;
789
790     if (self->entry == NULL) {
791         PyErr_SetString(PyExc_AttributeError, "entry attribute");
792         return NULL;
793     }
794     if((p = acl_get_qualifier(self->entry)) == NULL) {
795         PyErr_SetFromErrno(PyExc_IOError);
796         return NULL;
797     }
798     value = *(uid_t*)p;
799     acl_free(p);
800
801     return PyInt_FromLong(value);
802 }
803
804 /* Returns the parent ACL of the entry */
805 static PyObject* Entry_get_parent(PyObject *obj, void* arg) {
806     Entry_Object *self = (Entry_Object*) obj;
807
808     Py_INCREF(self->parent_acl);
809     return self->parent_acl;
810 }
811
812 /* Returns the a new Permset representing the permset of the entry
813  * FIXME: Should return a new reference to the same object, which
814  * should be created at init time!
815  */
816 static PyObject* Entry_get_permset(PyObject *obj, void* arg) {
817     Entry_Object *self = (Entry_Object*)obj;
818     PyObject *p;
819     Permset_Object *ps;
820
821     p = Permset_new(&Permset_Type, NULL, NULL);
822     if(p == NULL)
823         return NULL;
824     ps = (Permset_Object*)p;
825     if(acl_get_permset(self->entry, &ps->permset) == -1) {
826         PyErr_SetFromErrno(PyExc_IOError);
827         return NULL;
828     }
829     ps->parent_entry = obj;
830     Py_INCREF(obj);
831
832     return (PyObject*)p;
833 }
834
835 /* Sets the permset of the entry to the passed Permset */
836 static int Entry_set_permset(PyObject* obj, PyObject* value, void* arg) {
837     Entry_Object *self = (Entry_Object*)obj;
838     Permset_Object *p;
839
840     if(!PyObject_IsInstance(value, (PyObject*)&Permset_Type)) {
841         PyErr_SetString(PyExc_TypeError, "argument 1 must be posix1e.Permset");
842         return -1;
843     }
844     p = (Permset_Object*)value;
845     if(acl_set_permset(self->entry, p->permset) == -1) {
846         PyErr_SetFromErrno(PyExc_IOError);
847         return -1;
848     }
849     return 0;
850 }
851
852 static char __Entry_copy_doc__[] =
853     "Copy an ACL entry.\n"
854     "\n"
855     "This method sets all the parameters to those of another\n"
856     "entry, even one of another's ACL\n"
857     "Parameters:\n"
858     " - src, instance of type Entry\n"
859     ;
860
861 /* Sets all the entry parameters to another's entry */
862 static PyObject* Entry_copy(PyObject *obj, PyObject *args) {
863     Entry_Object *self = (Entry_Object*)obj;
864     Entry_Object *other;
865
866     if(!PyArg_ParseTuple(args, "O!", &Entry_Type, &other))
867         return NULL;
868
869     if(acl_copy_entry(self->entry, other->entry) == -1)
870         return PyErr_SetFromErrno(PyExc_IOError);
871
872     Py_INCREF(Py_None);
873     return Py_None;
874 }
875
876 /**** Permset type *****/
877
878 /* Creation of a new Permset instance */
879 static PyObject* Permset_new(PyTypeObject* type, PyObject* args,
880                              PyObject *keywds) {
881     PyObject* newpermset;
882
883     newpermset = PyType_GenericNew(type, args, keywds);
884
885     if(newpermset != NULL) {
886         ((Permset_Object*)newpermset)->permset = NULL;
887         ((Permset_Object*)newpermset)->parent_entry = NULL;
888     }
889
890     return newpermset;
891 }
892
893 /* Initialization of a new Permset instance */
894 static int Permset_init(PyObject* obj, PyObject* args, PyObject *keywds) {
895     Permset_Object* self = (Permset_Object*) obj;
896     Entry_Object* parent = NULL;
897
898     if (!PyArg_ParseTuple(args, "O!", &Entry_Type, &parent))
899         return -1;
900
901     if(acl_get_permset(parent->entry, &self->permset) == -1) {
902         PyErr_SetFromErrno(PyExc_IOError);
903         return -1;
904     }
905
906     self->parent_entry = (PyObject*)parent;
907     Py_INCREF(parent);
908
909     return 0;
910 }
911
912 /* Free the Permset instance */
913 static void Permset_dealloc(PyObject* obj) {
914     Permset_Object *self = (Permset_Object*) obj;
915     PyObject *err_type, *err_value, *err_traceback;
916     int have_error = PyErr_Occurred() ? 1 : 0;
917
918     if (have_error)
919         PyErr_Fetch(&err_type, &err_value, &err_traceback);
920     if(self->parent_entry != NULL) {
921         Py_DECREF(self->parent_entry);
922         self->parent_entry = NULL;
923     }
924     if (have_error)
925         PyErr_Restore(err_type, err_value, err_traceback);
926     PyObject_DEL(self);
927 }
928
929 /* Permset string representation */
930 static PyObject* Permset_str(PyObject *obj) {
931     Permset_Object *self = (Permset_Object*) obj;
932     char pstr[3];
933
934     pstr[0] = get_perm(self->permset, ACL_READ) ? 'r' : '-';
935     pstr[1] = get_perm(self->permset, ACL_WRITE) ? 'w' : '-';
936     pstr[2] = get_perm(self->permset, ACL_EXECUTE) ? 'x' : '-';
937     return PyString_FromStringAndSize(pstr, 3);
938 }
939
940 static char __Permset_clear_doc__[] =
941     "Clear all permissions from the permission set.\n"
942     ;
943
944 /* Clears all permissions from the permset */
945 static PyObject* Permset_clear(PyObject* obj, PyObject* args) {
946     Permset_Object *self = (Permset_Object*) obj;
947
948     if(acl_clear_perms(self->permset) == -1)
949         return PyErr_SetFromErrno(PyExc_IOError);
950
951     /* Return the result */
952     Py_INCREF(Py_None);
953     return Py_None;
954 }
955
956 static PyObject* Permset_get_right(PyObject *obj, void* arg) {
957     Permset_Object *self = (Permset_Object*) obj;
958
959     if(get_perm(self->permset, *(acl_perm_t*)arg)) {
960         Py_INCREF(Py_True);
961         return Py_True;
962     } else {
963         Py_INCREF(Py_False);
964         return Py_False;
965     }
966 }
967
968 static int Permset_set_right(PyObject* obj, PyObject* value, void* arg) {
969     Permset_Object *self = (Permset_Object*) obj;
970     int on;
971     int nerr;
972
973     if(!PyInt_Check(value)) {
974         PyErr_SetString(PyExc_ValueError, "a maximum of one argument must"
975                         " be passed");
976         return -1;
977     }
978     on = PyInt_AsLong(value);
979     if(on)
980         nerr = acl_add_perm(self->permset, *(acl_perm_t*)arg);
981     else
982         nerr = acl_delete_perm(self->permset, *(acl_perm_t*)arg);
983     if(nerr == -1) {
984         PyErr_SetFromErrno(PyExc_IOError);
985         return -1;
986     }
987     return 0;
988 }
989
990 static char __Permset_add_doc__[] =
991     "Add a permission to the permission set.\n"
992     "\n"
993     "The add() function adds the permission contained in \n"
994     "the argument perm to the permission set.  An attempt \n"
995     "to add a permission that is already contained in the \n"
996     "permission set is not considered an error.\n"
997     "\n"
998     "Parameters:\n\n"
999     "  - perm: a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1000     "\n"
1001     "Return value: None\n"
1002     "\n"
1003     "Can raise: IOError\n"
1004     ;
1005
1006 static PyObject* Permset_add(PyObject* obj, PyObject* args) {
1007     Permset_Object *self = (Permset_Object*) obj;
1008     int right;
1009
1010     if (!PyArg_ParseTuple(args, "i", &right))
1011         return NULL;
1012
1013     if(acl_add_perm(self->permset, (acl_perm_t) right) == -1)
1014         return PyErr_SetFromErrno(PyExc_IOError);
1015
1016     /* Return the result */
1017     Py_INCREF(Py_None);
1018     return Py_None;
1019 }
1020
1021 static char __Permset_delete_doc__[] =
1022     "Delete a permission from the permission set.\n"
1023     "\n"
1024     "The delete() function deletes the permission contained in \n"
1025     "the argument perm from the permission set.  An attempt \n"
1026     "to delete a permission that is not contained in the \n"
1027     "permission set is not considered an error.\n"
1028     "Parameters:\n\n"
1029     "  - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1030     "Return value: None\n"
1031     "\n"
1032     "Can raise: IOError\n"
1033     ;
1034
1035 static PyObject* Permset_delete(PyObject* obj, PyObject* args) {
1036     Permset_Object *self = (Permset_Object*) obj;
1037     int right;
1038
1039     if (!PyArg_ParseTuple(args, "i", &right))
1040         return NULL;
1041
1042     if(acl_delete_perm(self->permset, (acl_perm_t) right) == -1)
1043         return PyErr_SetFromErrno(PyExc_IOError);
1044
1045     /* Return the result */
1046     Py_INCREF(Py_None);
1047     return Py_None;
1048 }
1049
1050 static char __Permset_test_doc__[] =
1051     "Test if a permission exists in the permission set.\n"
1052     "\n"
1053     "The test() function tests if the permission contained in \n"
1054     "the argument perm exits the permission set.\n"
1055     "Parameters:\n\n"
1056     "  - perm a permission (ACL_WRITE, ACL_READ, ACL_EXECUTE, ...)\n"
1057     "Return value: Boolean\n"
1058     "\n"
1059     "Can raise: IOError\n"
1060     ;
1061
1062 static PyObject* Permset_test(PyObject* obj, PyObject* args) {
1063     Permset_Object *self = (Permset_Object*) obj;
1064     int right;
1065     int ret;
1066
1067     if (!PyArg_ParseTuple(args, "i", &right))
1068         return NULL;
1069
1070     ret = get_perm(self->permset, (acl_perm_t) right);
1071     if(ret == -1)
1072         return PyErr_SetFromErrno(PyExc_IOError);
1073
1074     if(ret) {
1075         Py_INCREF(Py_True);
1076         return Py_True;
1077     } else {
1078         Py_INCREF(Py_False);
1079         return Py_False;
1080     }
1081 }
1082
1083 #endif
1084
1085 static char __ACL_Type_doc__[] =
1086     "Type which represents a POSIX ACL\n"
1087     "\n"
1088     "Parameters (only one keword parameter should be provided):\n"
1089     "  - file=\"...\", meaning create ACL representing\n"
1090     "    the access ACL of that file\n"
1091     "  - filedef=\"...\", meaning create ACL representing\n"
1092     "    the default ACL of that directory\n"
1093     "  - fd=<int>, meaning create ACL representing\n"
1094     "    the access ACL of that file descriptor\n"
1095     "  - text=\"...\", meaning create ACL from a \n"
1096     "    textual description\n"
1097     "  - acl=<ACL instance>, meaning create a copy\n"
1098     "    of an existing ACL instance\n"
1099     "  - mode=<int>, meaning create an ACL from a numeric mode\n"
1100     "    (e.g. mode=0644) (this is valid only when the C library\n"
1101     "    provides the acl_from_mode call)\n"
1102     "\n"
1103     "If no parameters are passed, create an empty ACL; this\n"
1104     "makes sense only when your OS supports ACL modification\n"
1105     "(i.e. it implements full POSIX.1e support)\n"
1106     ;
1107
1108 /* ACL type methods */
1109 static PyMethodDef ACL_methods[] = {
1110     {"applyto", ACL_applyto, METH_VARARGS, __applyto_doc__},
1111     {"valid", ACL_valid, METH_NOARGS, __valid_doc__},
1112 #ifdef HAVE_LINUX
1113     {"to_any_text", (PyCFunction)ACL_to_any_text, METH_VARARGS | METH_KEYWORDS,
1114      __to_any_text_doc__},
1115     {"check", ACL_check, METH_NOARGS, __check_doc__},
1116     {"equiv_mode", ACL_equiv_mode, METH_NOARGS, __equiv_mode_doc__},
1117 #endif
1118 #ifdef HAVE_ACL_COPYEXT
1119     {"__getstate__", ACL_get_state, METH_NOARGS,
1120      "Dumps the ACL to an external format."},
1121     {"__setstate__", ACL_set_state, METH_VARARGS,
1122      "Loads the ACL from an external format."},
1123 #endif
1124 #ifdef HAVE_LEVEL2
1125     {"delete_entry", ACL_delete_entry, METH_VARARGS, __ACL_delete_entry_doc__},
1126     {"calc_mask", ACL_calc_mask, METH_NOARGS, __ACL_calc_mask_doc__},
1127     {"append", ACL_append, METH_VARARGS, __ACL_append_doc__},
1128 #endif
1129     {NULL, NULL, 0, NULL}
1130 };
1131
1132
1133 /* The definition of the ACL Type */
1134 static PyTypeObject ACL_Type = {
1135     PyObject_HEAD_INIT(NULL)
1136     0,
1137     "posix1e.ACL",
1138     sizeof(ACL_Object),
1139     0,
1140     ACL_dealloc,        /* tp_dealloc */
1141     0,                  /* tp_print */
1142     0,                  /* tp_getattr */
1143     0,                  /* tp_setattr */
1144     ACL_nocmp,          /* tp_compare */
1145     0,                  /* tp_repr */
1146     0,                  /* tp_as_number */
1147     0,                  /* tp_as_sequence */
1148     0,                  /* tp_as_mapping */
1149     0,                  /* tp_hash */
1150     0,                  /* tp_call */
1151     ACL_str,            /* tp_str */
1152     0,                  /* tp_getattro */
1153     0,                  /* tp_setattro */
1154     0,                  /* tp_as_buffer */
1155     Py_TPFLAGS_DEFAULT, /* tp_flags */
1156     __ACL_Type_doc__,   /* tp_doc */
1157     0,                  /* tp_traverse */
1158     0,                  /* tp_clear */
1159 #ifdef HAVE_LINUX
1160     ACL_richcompare,    /* tp_richcompare */
1161 #else
1162     0,                  /* tp_richcompare */
1163 #endif
1164     0,                  /* tp_weaklistoffset */
1165 #ifdef HAVE_LEVEL2
1166     ACL_iter,
1167     ACL_iternext,
1168 #else
1169     0,                  /* tp_iter */
1170     0,                  /* tp_iternext */
1171 #endif
1172     ACL_methods,        /* tp_methods */
1173     0,                  /* tp_members */
1174     0,                  /* tp_getset */
1175     0,                  /* tp_base */
1176     0,                  /* tp_dict */
1177     0,                  /* tp_descr_get */
1178     0,                  /* tp_descr_set */
1179     0,                  /* tp_dictoffset */
1180     ACL_init,           /* tp_init */
1181     0,                  /* tp_alloc */
1182     ACL_new,            /* tp_new */
1183 };
1184
1185 #ifdef HAVE_LEVEL2
1186
1187 /* Entry type methods */
1188 static PyMethodDef Entry_methods[] = {
1189     {"copy", Entry_copy, METH_VARARGS, __Entry_copy_doc__},
1190     {NULL, NULL, 0, NULL}
1191 };
1192
1193 static char __Entry_tagtype_doc__[] =
1194     "The tag type of the current entry\n"
1195     "\n"
1196     "This is one of:\n"
1197     " - ACL_UNDEFINED_TAG\n"
1198     " - ACL_USER_OBJ\n"
1199     " - ACL_USER\n"
1200     " - ACL_GROUP_OBJ\n"
1201     " - ACL_GROUP\n"
1202     " - ACL_MASK\n"
1203     " - ACL_OTHER\n"
1204     ;
1205
1206 static char __Entry_qualifier_doc__[] =
1207     "The qualifier of the current entry\n"
1208     "\n"
1209     "If the tag type is ACL_USER, this should be a user id.\n"
1210     "If the tag type if ACL_GROUP, this should be a group id.\n"
1211     "Else, it doesn't matter.\n"
1212     ;
1213
1214 static char __Entry_parent_doc__[] =
1215     "The parent ACL of this entry\n"
1216     ;
1217
1218 static char __Entry_permset_doc__[] =
1219     "The permission set of this ACL entry\n"
1220     ;
1221
1222 /* Entry getset */
1223 static PyGetSetDef Entry_getsets[] = {
1224     {"tag_type", Entry_get_tag_type, Entry_set_tag_type,
1225      __Entry_tagtype_doc__},
1226     {"qualifier", Entry_get_qualifier, Entry_set_qualifier,
1227      __Entry_qualifier_doc__},
1228     {"parent", Entry_get_parent, NULL, __Entry_parent_doc__},
1229     {"permset", Entry_get_permset, Entry_set_permset, __Entry_permset_doc__},
1230     {NULL}
1231 };
1232
1233 static char __Entry_Type_doc__[] =
1234     "Type which represents an entry in an ACL.\n"
1235     "\n"
1236     "The type exists only if the OS has full support for POSIX.1e\n"
1237     "Can be created either by:\n"
1238     "\n"
1239     "  >>> e = posix1e.Entry(myACL) # this creates a new entry in the ACL\n"
1240     "  >>> e = myACL.append() # another way for doing the same thing\n"
1241     "\n"
1242     "or by:\n"
1243     "  >>> for entry in myACL:\n"
1244     "  ...     print entry\n"
1245     "\n"
1246     "Note that the Entry keeps a reference to its ACL, so even if \n"
1247     "you delete the ACL, it won't be cleaned up and will continue to \n"
1248     "exist until its Entry(ies) will be deleted.\n"
1249     ;
1250 /* The definition of the Entry Type */
1251 static PyTypeObject Entry_Type = {
1252     PyObject_HEAD_INIT(NULL)
1253     0,
1254     "posix1e.Entry",
1255     sizeof(Entry_Object),
1256     0,
1257     Entry_dealloc,      /* tp_dealloc */
1258     0,                  /* tp_print */
1259     0,                  /* tp_getattr */
1260     0,                  /* tp_setattr */
1261     0,                  /* tp_compare */
1262     0,                  /* tp_repr */
1263     0,                  /* tp_as_number */
1264     0,                  /* tp_as_sequence */
1265     0,                  /* tp_as_mapping */
1266     0,                  /* tp_hash */
1267     0,                  /* tp_call */
1268     Entry_str,          /* tp_str */
1269     0,                  /* tp_getattro */
1270     0,                  /* tp_setattro */
1271     0,                  /* tp_as_buffer */
1272     Py_TPFLAGS_DEFAULT, /* tp_flags */
1273     __Entry_Type_doc__, /* tp_doc */
1274     0,                  /* tp_traverse */
1275     0,                  /* tp_clear */
1276     0,                  /* tp_richcompare */
1277     0,                  /* tp_weaklistoffset */
1278     0,                  /* tp_iter */
1279     0,                  /* tp_iternext */
1280     Entry_methods,      /* tp_methods */
1281     0,                  /* tp_members */
1282     Entry_getsets,      /* tp_getset */
1283     0,                  /* tp_base */
1284     0,                  /* tp_dict */
1285     0,                  /* tp_descr_get */
1286     0,                  /* tp_descr_set */
1287     0,                  /* tp_dictoffset */
1288     Entry_init,         /* tp_init */
1289     0,                  /* tp_alloc */
1290     Entry_new,          /* tp_new */
1291 };
1292
1293 /* Permset type methods */
1294 static PyMethodDef Permset_methods[] = {
1295     {"clear", Permset_clear, METH_NOARGS, __Permset_clear_doc__, },
1296     {"add", Permset_add, METH_VARARGS, __Permset_add_doc__, },
1297     {"delete", Permset_delete, METH_VARARGS, __Permset_delete_doc__, },
1298     {"test", Permset_test, METH_VARARGS, __Permset_test_doc__, },
1299     {NULL, NULL, 0, NULL}
1300 };
1301
1302 static char __Permset_execute_doc__[] =
1303     "Execute permsission\n"
1304     "\n"
1305     "This is a convenience method of access; the \n"
1306     "same effect can be achieved using the functions\n"
1307     "add(), test(), delete(), and those can take any \n"
1308     "permission defined by your platform.\n"
1309     ;
1310
1311 static char __Permset_read_doc__[] =
1312     "Read permsission\n"
1313     "\n"
1314     "This is a convenience method of access; the \n"
1315     "same effect can be achieved using the functions\n"
1316     "add(), test(), delete(), and those can take any \n"
1317     "permission defined by your platform.\n"
1318     ;
1319
1320 static char __Permset_write_doc__[] =
1321     "Write permsission\n"
1322     "\n"
1323     "This is a convenience method of access; the \n"
1324     "same effect can be achieved using the functions\n"
1325     "add(), test(), delete(), and those can take any \n"
1326     "permission defined by your platform.\n"
1327     ;
1328
1329 /* Permset getset */
1330 static PyGetSetDef Permset_getsets[] = {
1331     {"execute", Permset_get_right, Permset_set_right,
1332      __Permset_execute_doc__, &holder_ACL_EXECUTE},
1333     {"read", Permset_get_right, Permset_set_right,
1334      __Permset_read_doc__, &holder_ACL_READ},
1335     {"write", Permset_get_right, Permset_set_right,
1336      __Permset_write_doc__, &holder_ACL_WRITE},
1337     {NULL}
1338 };
1339
1340 static char __Permset_Type_doc__[] =
1341     "Type which represents the permission set in an ACL entry\n"
1342     "\n"
1343     "The type exists only if the OS has full support for POSIX.1e\n"
1344     "Can be retrieved either by:\n\n"
1345     ">>> perms = myEntry.permset\n"
1346     "\n"
1347     "or by:\n\n"
1348     ">>> perms = posix1e.Permset(myEntry)\n"
1349     "\n"
1350     "Note that the Permset keeps a reference to its Entry, so even if \n"
1351     "you delete the entry, it won't be cleaned up and will continue to \n"
1352     "exist until its Permset will be deleted.\n"
1353     ;
1354
1355 /* The definition of the Permset Type */
1356 static PyTypeObject Permset_Type = {
1357     PyObject_HEAD_INIT(NULL)
1358     0,
1359     "posix1e.Permset",
1360     sizeof(Permset_Object),
1361     0,
1362     Permset_dealloc,    /* tp_dealloc */
1363     0,                  /* tp_print */
1364     0,                  /* tp_getattr */
1365     0,                  /* tp_setattr */
1366     0,                  /* tp_compare */
1367     0,                  /* tp_repr */
1368     0,                  /* tp_as_number */
1369     0,                  /* tp_as_sequence */
1370     0,                  /* tp_as_mapping */
1371     0,                  /* tp_hash */
1372     0,                  /* tp_call */
1373     Permset_str,        /* tp_str */
1374     0,                  /* tp_getattro */
1375     0,                  /* tp_setattro */
1376     0,                  /* tp_as_buffer */
1377     Py_TPFLAGS_DEFAULT, /* tp_flags */
1378     __Permset_Type_doc__,/* tp_doc */
1379     0,                  /* tp_traverse */
1380     0,                  /* tp_clear */
1381     0,                  /* tp_richcompare */
1382     0,                  /* tp_weaklistoffset */
1383     0,                  /* tp_iter */
1384     0,                  /* tp_iternext */
1385     Permset_methods,    /* tp_methods */
1386     0,                  /* tp_members */
1387     Permset_getsets,    /* tp_getset */
1388     0,                  /* tp_base */
1389     0,                  /* tp_dict */
1390     0,                  /* tp_descr_get */
1391     0,                  /* tp_descr_set */
1392     0,                  /* tp_dictoffset */
1393     Permset_init,       /* tp_init */
1394     0,                  /* tp_alloc */
1395     Permset_new,        /* tp_new */
1396 };
1397
1398 #endif
1399
1400 /* Module methods */
1401
1402 static char __deletedef_doc__[] =
1403     "Delete the default ACL from a directory.\n"
1404     "\n"
1405     "This function deletes the default ACL associated with \n"
1406     "a directory (the ACL which will be ANDed with the mode\n"
1407     "parameter to the open, creat functions).\n"
1408     "Parameters:\n"
1409     "  - a string representing the directory whose default ACL\n"
1410     "    should be deleted\n"
1411     ;
1412
1413 /* Deletes the default ACL from a directory */
1414 static PyObject* aclmodule_delete_default(PyObject* obj, PyObject* args) {
1415     char *filename;
1416
1417     /* Parse the arguments */
1418     if (!PyArg_ParseTuple(args, "s", &filename))
1419         return NULL;
1420
1421     if(acl_delete_def_file(filename) == -1) {
1422         return PyErr_SetFromErrno(PyExc_IOError);
1423     }
1424
1425     /* Return the result */
1426     Py_INCREF(Py_None);
1427     return Py_None;
1428 }
1429
1430 #ifdef HAVE_LINUX
1431 static char __has_extended_doc__[] =
1432     "Check if a file or filehandle has an extended ACL.\n"
1433     "\n"
1434     "Parameter:\n"
1435     "  - either a filename or a file-like object or an integer; this\n"
1436     "    represents the filesystem object on which to act\n"
1437     ;
1438
1439 /* Check for extended ACL a file or fd */
1440 static PyObject* aclmodule_has_extended(PyObject* obj, PyObject* args) {
1441     PyObject *myarg;
1442     int nret;
1443     int fd;
1444
1445     if (!PyArg_ParseTuple(args, "O", &myarg))
1446         return NULL;
1447
1448     if(PyString_Check(myarg)) {
1449         const char *filename = PyString_AS_STRING(myarg);
1450         nret = acl_extended_file(filename);
1451     } else if((fd = PyObject_AsFileDescriptor(myarg)) != -1) {
1452         nret = acl_extended_fd(fd);
1453     } else {
1454         PyErr_SetString(PyExc_TypeError, "argument 1 must be string, int,"
1455                         " or file-like object");
1456         return 0;
1457     }
1458     if(nret == -1) {
1459         return PyErr_SetFromErrno(PyExc_IOError);
1460     }
1461
1462     /* Return the result */
1463     return PyBool_FromLong(nret);
1464 }
1465 #endif
1466
1467 /* The module methods */
1468 static PyMethodDef aclmodule_methods[] = {
1469     {"delete_default", aclmodule_delete_default, METH_VARARGS,
1470      __deletedef_doc__},
1471 #ifdef HAVE_LINUX
1472     {"has_extended", aclmodule_has_extended, METH_VARARGS,
1473      __has_extended_doc__},
1474 #endif
1475     {NULL, NULL, 0, NULL}
1476 };
1477
1478 static char __posix1e_doc__[] =
1479     "POSIX.1e ACLs manipulation\n"
1480     "\n"
1481     "This module provides support for manipulating POSIX.1e ACLS\n"
1482     "\n"
1483     "Depending on the operating system support for POSIX.1e, \n"
1484     "the ACL type will have more or less capabilities:\n"
1485     "  - level 1, only basic support, you can create\n"
1486     "    ACLs from files and text descriptions;\n"
1487     "    once created, the type is immutable\n"
1488     "  - level 2, complete support, you can alter\n"
1489     "    the ACL once it is created\n"
1490     "\n"
1491     "Also, in level 2, more types are available, corresponding\n"
1492     "to acl_entry_t (the Entry type), acl_permset_t (the Permset type).\n"
1493     "\n"
1494     "The existence of level 2 support and other extensions can be\n"
1495     "checked by the constants:\n"
1496     "  - HAS_ACL_ENTRY for level 2 and the Entry/Permset classes\n"
1497     "  - HAS_ACL_FROM_MODE for ACL(mode=...) usage\n"
1498     "  - HAS_ACL_CHECK for the ACL().check function\n"
1499     "  - HAS_EXTENDED_CHECK for the module-level has_extended function\n"
1500     "  - HAS_EQUIV_MODE for the ACL().equiv_mode method\n"
1501     "\n"
1502     "Example:\n"
1503     "\n"
1504     ">>> import posix1e\n"
1505     ">>> acl1 = posix1e.ACL(file=\"file.txt\") \n"
1506     ">>> print acl1\n"
1507     "user::rw-\n"
1508     "group::rw-\n"
1509     "other::r--\n"
1510     ">>>\n"
1511     ">>> b = posix1e.ACL(text=\"u::rx,g::-,o::-\")\n"
1512     ">>> print b\n"
1513     "user::r-x\n"
1514     "group::---\n"
1515     "other::---\n"
1516     ">>>\n"
1517     ">>> b.applyto(\"file.txt\")\n"
1518     ">>> print posix1e.ACL(file=\"file.txt\")\n"
1519     "user::r-x\n"
1520     "group::---\n"
1521     "other::---\n"
1522     ">>>\n"
1523     "\n"
1524     ;
1525
1526 void initposix1e(void) {
1527     PyObject *m, *d;
1528
1529     ACL_Type.ob_type = &PyType_Type;
1530     if(PyType_Ready(&ACL_Type) < 0)
1531         return;
1532
1533 #ifdef HAVE_LEVEL2
1534     Entry_Type.ob_type = &PyType_Type;
1535     if(PyType_Ready(&Entry_Type) < 0)
1536         return;
1537
1538     Permset_Type.ob_type = &PyType_Type;
1539     if(PyType_Ready(&Permset_Type) < 0)
1540         return;
1541 #endif
1542
1543     m = Py_InitModule3("posix1e", aclmodule_methods, __posix1e_doc__);
1544
1545     d = PyModule_GetDict(m);
1546     if (d == NULL)
1547         return;
1548
1549     Py_INCREF(&ACL_Type);
1550     if (PyDict_SetItemString(d, "ACL",
1551                              (PyObject *) &ACL_Type) < 0)
1552         return;
1553
1554     /* 23.3.6 acl_type_t values */
1555     PyModule_AddIntConstant(m, "ACL_TYPE_ACCESS", ACL_TYPE_ACCESS);
1556     PyModule_AddIntConstant(m, "ACL_TYPE_DEFAULT", ACL_TYPE_DEFAULT);
1557
1558
1559 #ifdef HAVE_LEVEL2
1560     Py_INCREF(&Entry_Type);
1561     if (PyDict_SetItemString(d, "Entry",
1562                              (PyObject *) &Entry_Type) < 0)
1563         return;
1564
1565     Py_INCREF(&Permset_Type);
1566     if (PyDict_SetItemString(d, "Permset",
1567                              (PyObject *) &Permset_Type) < 0)
1568         return;
1569
1570     /* 23.2.2 acl_perm_t values */
1571     PyModule_AddIntConstant(m, "ACL_READ", ACL_READ);
1572     PyModule_AddIntConstant(m, "ACL_WRITE", ACL_WRITE);
1573     PyModule_AddIntConstant(m, "ACL_EXECUTE", ACL_EXECUTE);
1574
1575     /* 23.2.5 acl_tag_t values */
1576     PyModule_AddIntConstant(m, "ACL_UNDEFINED_TAG", ACL_UNDEFINED_TAG);
1577     PyModule_AddIntConstant(m, "ACL_USER_OBJ", ACL_USER_OBJ);
1578     PyModule_AddIntConstant(m, "ACL_USER", ACL_USER);
1579     PyModule_AddIntConstant(m, "ACL_GROUP_OBJ", ACL_GROUP_OBJ);
1580     PyModule_AddIntConstant(m, "ACL_GROUP", ACL_GROUP);
1581     PyModule_AddIntConstant(m, "ACL_MASK", ACL_MASK);
1582     PyModule_AddIntConstant(m, "ACL_OTHER", ACL_OTHER);
1583
1584     /* Document extended functionality via easy-to-use constants */
1585     PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 1);
1586 #else
1587     PyModule_AddIntConstant(m, "HAS_ACL_ENTRY", 0);
1588 #endif
1589
1590 #ifdef HAVE_LINUX
1591     /* Linux libacl specific acl_to_any_text constants */
1592     PyModule_AddIntConstant(m, "TEXT_ABBREVIATE", TEXT_ABBREVIATE);
1593     PyModule_AddIntConstant(m, "TEXT_NUMERIC_IDS", TEXT_NUMERIC_IDS);
1594     PyModule_AddIntConstant(m, "TEXT_SOME_EFFECTIVE", TEXT_SOME_EFFECTIVE);
1595     PyModule_AddIntConstant(m, "TEXT_ALL_EFFECTIVE", TEXT_ALL_EFFECTIVE);
1596     PyModule_AddIntConstant(m, "TEXT_SMART_INDENT", TEXT_SMART_INDENT);
1597
1598     /* Linux libacl specific acl_check constants */
1599     PyModule_AddIntConstant(m, "ACL_MULTI_ERROR", ACL_MULTI_ERROR);
1600     PyModule_AddIntConstant(m, "ACL_DUPLICATE_ERROR", ACL_DUPLICATE_ERROR);
1601     PyModule_AddIntConstant(m, "ACL_MISS_ERROR", ACL_MISS_ERROR);
1602     PyModule_AddIntConstant(m, "ACL_ENTRY_ERROR", ACL_ENTRY_ERROR);
1603
1604     /* declare the Linux extensions */
1605     PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", 1);
1606     PyModule_AddIntConstant(m, "HAS_ACL_CHECK", 1);
1607     PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", 1);
1608     PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", 1);
1609 #else
1610     PyModule_AddIntConstant(m, "HAS_ACL_FROM_MODE", 0);
1611     PyModule_AddIntConstant(m, "HAS_ACL_CHECK", 0);
1612     PyModule_AddIntConstant(m, "HAS_EXTENDED_CHECK", 0);
1613     PyModule_AddIntConstant(m, "HAS_EQUIV_MODE", 0);
1614 #endif
1615 }