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