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