fix msm-plugin.c svace issue: make sure dupPath is not NULL before strchr()
[platform/upstream/rpm.git] / python / rpmfd-py.c
1
2 #include "rpmsystem-py.h"
3 #include <rpm/rpmstring.h>
4 #include "header-py.h"  /* XXX for utf8FromPyObject() only */
5 #include "rpmfd-py.h"
6
7 struct rpmfdObject_s {
8     PyObject_HEAD
9     PyObject *md_dict;
10     FD_t fd;
11     char *mode;
12     char *flags;
13 };
14
15 FD_t rpmfdGetFd(rpmfdObject *fdo)
16 {
17     return fdo->fd;
18 }
19
20 int rpmfdFromPyObject(PyObject *obj, rpmfdObject **fdop)
21 {
22     rpmfdObject *fdo = NULL;
23
24     if (rpmfdObject_Check(obj)) {
25         Py_INCREF(obj);
26         fdo = (rpmfdObject *) obj;
27     } else {
28         fdo = (rpmfdObject *) PyObject_CallFunctionObjArgs((PyObject *)&rpmfd_Type,
29                                                            obj, NULL);
30     }
31     if (fdo == NULL) return 0;
32
33     if (Ferror(fdo->fd)) {
34         PyErr_SetString(PyExc_IOError, Fstrerror(fdo->fd));
35         Py_DECREF(fdo);
36         return 0;
37     }
38     *fdop = fdo;
39     return 1;
40 }
41
42 static PyObject *err_closed(void)
43 {
44     PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
45     return NULL;
46 }
47
48 static FD_t openPath(const char *path, const char *mode)
49 {
50     FD_t fd;
51     Py_BEGIN_ALLOW_THREADS
52     fd = Fopen(path, mode);
53     Py_END_ALLOW_THREADS;
54     return fd;
55 }
56
57 static FD_t openFd(FD_t ofd, const char *mode)
58 {
59     FD_t fd;
60     Py_BEGIN_ALLOW_THREADS
61     fd = Fdopen(ofd, mode);
62     Py_END_ALLOW_THREADS;
63     return fd;
64 }
65
66 static int rpmfd_init(rpmfdObject *s, PyObject *args, PyObject *kwds)
67 {
68     char *kwlist[] = { "obj", "mode", "flags", NULL };
69     const char *mode = "r";
70     const char *flags = "ufdio";
71     char *rpmio_mode = NULL;
72     PyObject *fo = NULL;
73     FD_t fd = NULL;
74     int fdno;
75
76     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ss", kwlist, 
77                                      &fo, &mode, &flags))
78         return -1;
79
80     rpmio_mode = rstrscat(NULL, mode, ".", flags, NULL);
81
82     if (PyBytes_Check(fo)) {
83         fd = openPath(PyBytes_AsString(fo), rpmio_mode);
84     } else if (PyUnicode_Check(fo)) {
85         PyObject *enc = NULL;
86         int rc;
87 #if PY_MAJOR_VERSION >= 3
88         rc = PyUnicode_FSConverter(fo, &enc);
89 #else
90         rc = utf8FromPyObject(fo, &enc);
91 #endif
92         if (rc) {
93             fd = openPath(PyBytes_AsString(enc), rpmio_mode);
94             Py_DECREF(enc);
95         }
96     } else if (rpmfdObject_Check(fo)) {
97         rpmfdObject *fdo = (rpmfdObject *)fo;
98         fd = openFd(fdDup(Fileno(fdo->fd)), rpmio_mode);
99     } else if ((fdno = PyObject_AsFileDescriptor(fo)) >= 0) {
100         fd = openFd(fdDup(fdno), rpmio_mode);
101     } else {
102         PyErr_SetString(PyExc_TypeError, "path or file object expected");
103     }
104
105     if (fd != NULL) {
106         Fclose(s->fd); /* in case __init__ was called again */
107         free(s->mode);
108         free(s->flags);
109         s->fd = fd;
110         s->mode = rstrdup(mode);
111         s->flags = rstrdup(flags);
112     } else {
113         PyErr_SetString(PyExc_IOError, Fstrerror(fd));
114     }
115
116     free(rpmio_mode);
117     return (fd == NULL) ? -1 : 0;
118 }
119
120 static PyObject *rpmfd_open(PyObject *cls, PyObject *args, PyObject *kwds)
121 {
122     return PyObject_Call(cls, args, kwds);
123 }
124
125 static PyObject *do_close(rpmfdObject *s)
126 {
127     /* mimic python fileobject: close on closed file is not an error */
128     if (s->fd) {
129         Py_BEGIN_ALLOW_THREADS
130         Fclose(s->fd);
131         Py_END_ALLOW_THREADS
132         s->fd = NULL;
133     }
134     Py_RETURN_NONE;
135 }
136
137 static PyObject *rpmfd_close(rpmfdObject *s)
138 {
139     return do_close(s);
140 }
141
142 static void rpmfd_dealloc(rpmfdObject *s)
143 {
144     PyObject *res = do_close(s);
145     Py_XDECREF(res);
146     free(s->mode);
147     free(s->flags);
148     Py_TYPE(s)->tp_free((PyObject *)s);
149 }
150
151 static PyObject *rpmfd_fileno(rpmfdObject *s)
152 {
153     int fno;
154     if (s->fd == NULL) return err_closed();
155
156     Py_BEGIN_ALLOW_THREADS
157     fno = Fileno(s->fd);
158     Py_END_ALLOW_THREADS
159
160     if (Ferror(s->fd)) {
161         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
162         return NULL;
163     }
164     return Py_BuildValue("i", fno);
165 }
166
167 static PyObject *rpmfd_flush(rpmfdObject *s)
168 {
169     int rc;
170
171     if (s->fd == NULL) return err_closed();
172
173     Py_BEGIN_ALLOW_THREADS
174     rc = Fflush(s->fd);
175     Py_END_ALLOW_THREADS
176
177     if (rc || Ferror(s->fd)) {
178         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
179         return NULL;
180     }
181     Py_RETURN_NONE;
182 }
183
184 static PyObject *rpmfd_isatty(rpmfdObject *s)
185 {
186     int fileno;
187     if (s->fd == NULL) return err_closed();
188
189     Py_BEGIN_ALLOW_THREADS
190     fileno = Fileno(s->fd);
191     Py_END_ALLOW_THREADS
192
193     if (Ferror(s->fd)) {
194         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
195         return NULL;
196     }
197     return PyBool_FromLong(isatty(fileno));
198 }
199
200 static PyObject *rpmfd_seek(rpmfdObject *s, PyObject *args, PyObject *kwds)
201 {
202     char *kwlist[] = { "offset", "whence", NULL };
203     off_t offset;
204     int whence = SEEK_SET;
205     int rc = 0;
206
207     if (!PyArg_ParseTupleAndKeywords(args, kwds, "L|i", kwlist, 
208                                      &offset, &whence)) 
209         return NULL;
210
211     if (s->fd == NULL) return err_closed();
212
213     Py_BEGIN_ALLOW_THREADS
214     rc = Fseek(s->fd, offset, whence);
215     Py_END_ALLOW_THREADS
216     if (rc < 0 || Ferror(s->fd)) {
217         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
218         return NULL;
219     }
220     Py_RETURN_NONE;
221 }
222
223 static PyObject *rpmfd_tell(rpmfdObject *s)
224 {
225     off_t offset;
226     Py_BEGIN_ALLOW_THREADS
227     offset = Ftell(s->fd);
228     Py_END_ALLOW_THREADS
229     return Py_BuildValue("L", offset);
230     
231 }
232
233 static PyObject *rpmfd_read(rpmfdObject *s, PyObject *args, PyObject *kwds)
234 {
235     char *kwlist[] = { "size", NULL };
236     char buf[BUFSIZ];
237     ssize_t chunksize = sizeof(buf);
238     ssize_t left = -1;
239     ssize_t nb = 0;
240     PyObject *res = NULL;
241     
242     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|l", kwlist, &left))
243         return NULL;
244
245     if (s->fd == NULL) return err_closed();
246
247     /* ConcatAndDel() doesn't work on NULL string, meh */
248     res = PyBytes_FromStringAndSize(NULL, 0);
249     do {
250         if (left >= 0 && left < chunksize)
251             chunksize = left;
252
253         Py_BEGIN_ALLOW_THREADS 
254         nb = Fread(buf, 1, chunksize, s->fd);
255         Py_END_ALLOW_THREADS 
256
257         if (nb > 0) {
258             PyObject *tmp = PyBytes_FromStringAndSize(buf, nb);
259             PyBytes_ConcatAndDel(&res, tmp);
260             left -= nb;
261         }
262     } while (nb > 0);
263
264     if (Ferror(s->fd)) {
265         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
266         Py_XDECREF(res);
267         return NULL;
268     } else {
269         return res;
270     }
271 }
272
273 static PyObject *rpmfd_write(rpmfdObject *s, PyObject *args, PyObject *kwds)
274 {
275
276     const char *buf = NULL;
277     ssize_t size = 0;
278     char *kwlist[] = { "buffer", NULL };
279     ssize_t rc = 0;
280
281     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#", kwlist, &buf, &size)) {
282         return NULL;
283     }
284
285     if (s->fd == NULL) return err_closed();
286
287     Py_BEGIN_ALLOW_THREADS 
288     rc = Fwrite(buf, 1, size, s->fd);
289     Py_END_ALLOW_THREADS 
290
291     if (Ferror(s->fd)) {
292         PyErr_SetString(PyExc_IOError, Fstrerror(s->fd));
293         return NULL;
294     }
295     return Py_BuildValue("n", rc);
296 }
297
298 static char rpmfd_doc[] = "";
299
300 static struct PyMethodDef rpmfd_methods[] = {
301     { "open",   (PyCFunction) rpmfd_open,       METH_VARARGS|METH_KEYWORDS|METH_CLASS,
302         NULL },
303     { "close",  (PyCFunction) rpmfd_close,      METH_NOARGS,
304         NULL },
305     { "fileno", (PyCFunction) rpmfd_fileno,     METH_NOARGS,
306         NULL },
307     { "flush",  (PyCFunction) rpmfd_flush,      METH_NOARGS,
308         NULL },
309     { "isatty", (PyCFunction) rpmfd_isatty,     METH_NOARGS,
310         NULL },
311     { "read",   (PyCFunction) rpmfd_read,       METH_VARARGS|METH_KEYWORDS,
312         NULL },
313     { "seek",   (PyCFunction) rpmfd_seek,       METH_VARARGS|METH_KEYWORDS,
314         NULL },
315     { "tell",   (PyCFunction) rpmfd_tell,       METH_NOARGS,
316         NULL },
317     { "write",  (PyCFunction) rpmfd_write,      METH_VARARGS|METH_KEYWORDS,
318         NULL },
319     { NULL, NULL }
320 };
321
322 static PyObject *rpmfd_get_closed(rpmfdObject *s)
323 {
324     return PyBool_FromLong((s->fd == NULL));
325 }
326
327 static PyObject *rpmfd_get_name(rpmfdObject *s)
328 {
329     /* XXX: rpm returns non-paths with [mumble], python files use <mumble> */
330     return Py_BuildValue("s", Fdescr(s->fd));
331 }
332
333 static PyObject *rpmfd_get_mode(rpmfdObject *s)
334 {
335     return Py_BuildValue("s", s->mode);
336 }
337
338 static PyObject *rpmfd_get_flags(rpmfdObject *s)
339 {
340     return Py_BuildValue("s", s->flags);
341 }
342
343 static PyGetSetDef rpmfd_getseters[] = {
344     { "closed", (getter)rpmfd_get_closed, NULL, NULL },
345     { "name", (getter)rpmfd_get_name, NULL, NULL },
346     { "mode", (getter)rpmfd_get_mode, NULL, NULL },
347     { "flags", (getter)rpmfd_get_flags, NULL, NULL },
348     { NULL },
349 };
350
351 PyTypeObject rpmfd_Type = {
352         PyVarObject_HEAD_INIT(&PyType_Type, 0)
353         "rpm.fd",                       /* tp_name */
354         sizeof(rpmfdObject),            /* tp_size */
355         0,                              /* tp_itemsize */
356         /* methods */
357         (destructor) rpmfd_dealloc,     /* tp_dealloc */
358         0,                              /* tp_print */
359         (getattrfunc)0,                 /* tp_getattr */
360         (setattrfunc)0,                 /* tp_setattr */
361         0,                              /* tp_compare */
362         (reprfunc)0,                    /* tp_repr */
363         0,                              /* tp_as_number */
364         0,                              /* tp_as_sequence */
365         0,                              /* tp_as_mapping */
366         (hashfunc)0,                    /* tp_hash */
367         (ternaryfunc)0,                 /* tp_call */
368         (reprfunc)0,                    /* tp_str */
369         PyObject_GenericGetAttr,        /* tp_getattro */
370         PyObject_GenericSetAttr,        /* tp_setattro */
371         0,                              /* tp_as_buffer */
372         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
373         rpmfd_doc,                      /* tp_doc */
374         0,                              /* tp_traverse */
375         0,                              /* tp_clear */
376         0,                              /* tp_richcompare */
377         0,                              /* tp_weaklistoffset */
378         0,                              /* tp_iter */
379         0,                              /* tp_iternext */
380         rpmfd_methods,                  /* tp_methods */
381         0,                              /* tp_members */
382         rpmfd_getseters,                /* tp_getset */
383         0,                              /* tp_base */
384         0,                              /* tp_dict */
385         0,                              /* tp_descr_get */
386         0,                              /* tp_descr_set */
387         0,                              /* tp_dictoffset */
388         (initproc)rpmfd_init,           /* tp_init */
389         (allocfunc)0,                   /* tp_alloc */
390         PyType_GenericNew,              /* tp_new */
391         (freefunc)0,                    /* tp_free */
392         0,                              /* tp_is_gc */
393 };