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