fix undefined symbol utf8FromString in _rpmb module when frpm rpm_tizen import _rpmb
[platform/upstream/rpm.git] / python / header-py.c
1 #include "rpmsystem-py.h"
2
3 #include <rpm/rpmlib.h>         /* rpmvercmp */
4 #include <rpm/rpmtag.h>
5 #include <rpm/rpmstring.h>
6 #include <rpm/rpmts.h>  /* XXX rpmtsCreate/rpmtsFree */
7
8 #include "header-py.h"
9 #include "rpmds-py.h"
10 #include "rpmfd-py.h"
11 #include "rpmfi-py.h"
12 #include "rpmtd-py.h"
13
14 /** \ingroup python
15  * \class Rpm
16  * \brief START HERE / RPM base module for the Python API
17  *
18  * The rpm base module provides the main starting point for
19  * accessing RPM from Python. For most usage, call
20  * the TransactionSet method to get a transaction set (rpmts).
21  *
22  * For example:
23  * \code
24  *      import rpm
25  *
26  *      ts = rpm.TransactionSet()
27  * \endcode
28  *
29  * The transaction set will open the RPM database as needed, so
30  * in most cases, you do not need to explicitly open the
31  * database. The transaction set is the workhorse of RPM.
32  *
33  * You can open another RPM database, such as one that holds
34  * all packages for a given Linux distribution, to provide
35  * packages used to solve dependencies. To do this, use
36  * the following code:
37  *
38  * \code
39  * rpm.addMacro('_dbpath', '/path/to/alternate/database')
40  * solvets = rpm.TransactionSet()
41  * solvets.openDB()
42  * rpm.delMacro('_dbpath')
43  *
44  * # Open default database
45  * ts = rpm.TransactionSet()
46  * \endcode
47  *
48  * This code gives you access to two RPM databases through
49  * two transaction sets (rpmts): ts is a transaction set
50  * associated with the default RPM database and solvets
51  * is a transaction set tied to an alternate database, which
52  * is very useful for resolving dependencies.
53  *
54  * The rpm methods used here are:
55  *
56  * - addMacro(macro, value)
57  * @param macro   Name of macro to add
58  * @param value   Value for the macro
59  *
60  * - delMacro(macro)
61  * @param macro   Name of macro to delete
62  *
63  */
64
65 /** \ingroup python
66  * \class Rpmhdr
67  * \brief A python header object represents an RPM package header.
68  *
69  * All RPM packages have headers that provide metadata for the package.
70  * Header objects can be returned by database queries or loaded from a
71  * binary package on disk.
72  *
73  * The ts.hdrFromFdno() function returns the package header from a
74  * package on disk, verifying package signatures and digests of the
75  * package while reading.
76  *
77  * Note: The older method rpm.headerFromPackage() which has been replaced
78  * by ts.hdrFromFdno() used to return a (hdr, isSource) tuple.
79  *
80  * If you need to distinguish source/binary headers, do:
81  * \code
82  *      import os, rpm
83  *
84  *      ts = rpm.TransactionSet()
85  *      fdno = os.open("/tmp/foo-1.0-1.i386.rpm", os.O_RDONLY)
86  *      hdr = ts.hdrFromFdno(fdno)
87  *      os.close(fdno)
88  *      if hdr[rpm.RPMTAG_SOURCEPACKAGE]:
89  *         print "header is from a source package"
90  *      else:
91  *         print "header is from a binary package"
92  * \endcode
93  *
94  * The Python interface to the header data is quite elegant.  It
95  * presents the data in a dictionary form.  We'll take the header we
96  * just loaded and access the data within it:
97  * \code
98  *      print hdr[rpm.RPMTAG_NAME]
99  *      print hdr[rpm.RPMTAG_VERSION]
100  *      print hdr[rpm.RPMTAG_RELEASE]
101  * \endcode
102  * in the case of our "foo-1.0-1.i386.rpm" package, this code would
103  * output:
104 \verbatim
105         foo
106         1.0
107         1
108 \endverbatim
109  *
110  * You make also access the header data by string name:
111  * \code
112  *      print hdr['name']
113  *      print hdr['version']
114  *      print hdr['release']
115  * \endcode
116  *
117  * This method of access is a teensy bit slower because the name must be
118  * translated into the tag number dynamically. You also must make sure
119  * the strings in header lookups don't get translated, or the lookups
120  * will fail.
121  */
122
123 /** \ingroup python
124  * \name Class: rpm.hdr
125  */
126
127 struct hdrObject_s {
128     PyObject_HEAD
129     Header h;
130     HeaderIterator hi;
131 };
132
133 static PyObject * hdrKeyList(hdrObject * s)
134 {
135     PyObject * keys;
136     HeaderIterator hi;
137     rpmTagVal tag;
138
139     keys = PyList_New(0);
140     if (!keys) {
141         return NULL;
142     }
143
144     hi = headerInitIterator(s->h);
145     while ((tag = headerNextTag(hi)) != RPMTAG_NOT_FOUND) {
146         PyObject *to = PyLong_FromLong(tag);
147         if (!to) {
148             headerFreeIterator(hi);
149             Py_DECREF(keys);
150             return NULL;
151         }
152         PyList_Append(keys, to);
153         Py_DECREF(to);
154     }
155     headerFreeIterator(hi);
156
157     return keys;
158 }
159
160 static PyObject * hdrAsBytes(hdrObject * s)
161 {
162     PyObject *res = NULL;
163     unsigned int len = 0;
164     char *buf = headerExport(s->h, &len);
165
166     if (buf == NULL || len == 0) {
167         PyErr_SetString(pyrpmError, "can't unload bad header\n");
168     } else {
169         res = PyBytes_FromStringAndSize(buf, len);
170     }
171     free(buf);
172     return res;
173 }
174
175 static PyObject * hdrUnload(hdrObject * s)
176 {
177     return hdrAsBytes(s);
178 }
179
180 static PyObject * hdrExpandFilelist(hdrObject * s)
181 {
182     DEPRECATED_METHOD("use hdr.convert() instead");
183     headerConvert(s->h, HEADERCONV_EXPANDFILELIST);
184
185     Py_RETURN_NONE;
186 }
187
188 static PyObject * hdrCompressFilelist(hdrObject * s)
189 {
190     DEPRECATED_METHOD("use hdr.convert() instead");
191     headerConvert(s->h, HEADERCONV_COMPRESSFILELIST);
192
193     Py_RETURN_NONE;
194 }
195
196 /* make a header with _all_ the tags we need */
197 static PyObject * hdrFullFilelist(hdrObject * s)
198 {
199     rpmtd fileNames = rpmtdNew();
200     Header h = s->h;
201
202     DEPRECATED_METHOD("obsolete method");
203     if (!headerIsEntry (h, RPMTAG_BASENAMES)
204         || !headerIsEntry (h, RPMTAG_DIRNAMES)
205         || !headerIsEntry (h, RPMTAG_DIRINDEXES))
206         headerConvert(h, HEADERCONV_COMPRESSFILELIST);
207
208     if (headerGet(h, RPMTAG_FILENAMES, fileNames, HEADERGET_EXT)) {
209         rpmtdSetTag(fileNames, RPMTAG_OLDFILENAMES);
210         headerPut(h, fileNames, HEADERPUT_DEFAULT);
211     }
212     rpmtdFree(fileNames);
213
214     Py_RETURN_NONE;
215 }
216
217 static PyObject * hdrFormat(hdrObject * s, PyObject * args, PyObject * kwds)
218 {
219     const char * fmt;
220     char * r;
221     errmsg_t err;
222     PyObject * result;
223     char * kwlist[] = {"format", NULL};
224
225     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &fmt))
226         return NULL;
227
228     r = headerFormat(s->h, fmt, &err);
229     if (!r) {
230         PyErr_SetString(pyrpmError, err);
231         return NULL;
232     }
233
234     result = utf8FromString(r);
235     free(r);
236
237     return result;
238 }
239
240 static PyObject *hdrIsSource(hdrObject *s)
241 {
242     return PyBool_FromLong(headerIsSource(s->h));
243 }
244
245 static int hdrContains(hdrObject *s, PyObject *pytag)
246 {
247     rpmTagVal tag;
248     if (!tagNumFromPyObject(pytag, &tag)) return -1;
249
250     return headerIsEntry(s->h, tag);
251 }
252
253 static PyObject *hdrConvert(hdrObject *self, PyObject *args, PyObject *kwds)
254 {
255     char *kwlist[] = {"op", NULL};
256     int op = -1;
257
258     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &op)) {
259         return NULL;
260     }
261     return PyBool_FromLong(headerConvert(self->h, op));
262 }
263
264 static PyObject * hdrWrite(hdrObject *s, PyObject *args, PyObject *kwds)
265 {
266     char *kwlist[] = { "file", "magic", NULL };
267     int magic = HEADER_MAGIC_YES;
268     rpmfdObject *fdo = NULL;
269     int rc;
270
271     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|i", kwlist,
272                                      rpmfdFromPyObject, &fdo, &magic))
273         return NULL;
274
275     Py_BEGIN_ALLOW_THREADS;
276     rc = headerWrite(rpmfdGetFd(fdo), s->h,
277                      magic ? HEADER_MAGIC_YES : HEADER_MAGIC_NO);
278     Py_END_ALLOW_THREADS;
279
280     if (rc) PyErr_SetFromErrno(PyExc_IOError);
281     Py_XDECREF(fdo); /* avoid messing up errno with file close  */
282     if (rc) return NULL;
283
284     Py_RETURN_NONE;
285 }
286
287 /*
288  * Just a backwards-compatibility dummy, the arguments are not looked at
289  * or used. TODO: push this over to python side...
290  */
291 static PyObject * hdr_fiFromHeader(PyObject * s, PyObject * args, PyObject * kwds)
292 {
293     return PyObject_CallFunctionObjArgs((PyObject *) &rpmfi_Type, s, NULL);
294 }
295
296 /* Backwards compatibility. Flags argument is just a dummy and discarded. */
297 static PyObject * hdr_dsFromHeader(PyObject * s, PyObject * args, PyObject * kwds)
298 {
299     rpmTagVal tag = RPMTAG_REQUIRENAME;
300     rpmsenseFlags flags = 0;
301     char * kwlist[] = {"to", "flags", NULL};
302     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&i:dsFromHeader", kwlist,
303             tagNumFromPyObject, &tag, &flags))
304         return NULL;
305
306     return PyObject_CallFunction((PyObject *) &rpmds_Type,
307                                  "(Oi)", s, tag);
308 }
309
310 static PyObject * hdr_dsOfHeader(PyObject * s)
311 {
312     return PyObject_CallFunction((PyObject *) &rpmds_Type,
313                                  "(Oi)", s, RPMTAG_NEVR);
314 }
315
316 static long hdr_hash(PyObject * h)
317 {
318     return (long) h;
319 }
320
321 static PyObject * hdr_reduce(hdrObject *s)
322 {
323     PyObject *res = NULL;
324     PyObject *blob = hdrAsBytes(s);
325     if (blob) {
326         res = Py_BuildValue("O(O)", Py_TYPE(s), blob);
327         Py_DECREF(blob);
328     }
329     return res;
330 }
331
332 static struct PyMethodDef hdr_methods[] = {
333     {"keys",            (PyCFunction) hdrKeyList,       METH_NOARGS,
334      "hdr.keys() -- Return a list of the header's rpm tags (int RPMTAG_*)." },
335     {"unload",          (PyCFunction) hdrUnload,        METH_NOARGS,
336      "hdr.unload() -- Return binary representation\nof the header." },
337     {"expandFilelist",  (PyCFunction) hdrExpandFilelist,METH_NOARGS,
338      "DEPRECATED -- Use hdr.convert() instead." },
339     {"compressFilelist",(PyCFunction) hdrCompressFilelist,METH_NOARGS,
340      "DEPRECATED -- Use hdr.convert() instead." },
341     {"fullFilelist",    (PyCFunction) hdrFullFilelist,  METH_NOARGS,
342      "DEPRECATED -- Obsolete method."},
343     {"convert",         (PyCFunction) hdrConvert,       METH_VARARGS|METH_KEYWORDS,
344      "hdr.convert(op=-1) -- Convert header - See HEADERCONV_*\nfor possible values of op."},
345     {"format",          (PyCFunction) hdrFormat,        METH_VARARGS|METH_KEYWORDS,
346      "hdr.format(format) -- Expand a query string with the header data.\n\nSee rpm -q for syntax." },
347     {"sprintf",         (PyCFunction) hdrFormat,        METH_VARARGS|METH_KEYWORDS,
348      "Alias for .format()." },
349     {"isSource",        (PyCFunction)hdrIsSource,       METH_NOARGS, 
350      "hdr.isSource() -- Return if header describes a source package." },
351     {"write",           (PyCFunction)hdrWrite,          METH_VARARGS|METH_KEYWORDS,
352      "hdr.write(file, magic=True) -- Write header to file." },
353     {"dsOfHeader",      (PyCFunction)hdr_dsOfHeader,    METH_NOARGS,
354      "hdr.dsOfHeader() -- Return dependency set with the header's NEVR."},
355     {"dsFromHeader",    (PyCFunction)hdr_dsFromHeader,  METH_VARARGS|METH_KEYWORDS,
356      "hdr.dsFromHeader(to=RPMTAG_REQUIRENAME, flags=None)\nGet dependency set from header. to must be one of the NAME tags\nbelonging to a dependency:\n'Providename', 'Requirename', 'Obsoletename', 'Conflictname',\n'Triggername', 'Recommendname', 'Suggestname', 'Supplementname',\n'Enhancename' or one of the corresponding RPMTAG_*NAME constants." },
357     {"fiFromHeader",    (PyCFunction)hdr_fiFromHeader,  METH_VARARGS|METH_KEYWORDS,
358      "hdr.fiFromHeader() -- Return rpm.fi object containing the file\nmeta data from the header.\n\nDEPRECATED - Use rpm.files(hdr) instead."},
359     {"__reduce__",      (PyCFunction)hdr_reduce,        METH_NOARGS,
360         NULL},
361
362     {NULL,              NULL}           /* sentinel */
363 };
364
365 /* TODO: permit keyring check + retrofits on copy/load */
366 static PyObject *hdr_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
367 {
368     PyObject *obj = NULL;
369     rpmfdObject *fdo = NULL;
370     Header h = NULL;
371     char *kwlist[] = { "obj", NULL };
372
373     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &obj)) {
374         return NULL;
375     }
376
377     if (obj == NULL) {
378         h = headerNew();
379     } else if (CAPSULE_CHECK(obj)) {
380         h = CAPSULE_EXTRACT(obj, "rpm._C_Header");
381         headerLink(h);
382     } else if (hdrObject_Check(obj)) {
383         h = headerCopy(((hdrObject*) obj)->h);
384     } else if (PyBytes_Check(obj)) {
385         h = headerCopyLoad(PyBytes_AsString(obj));
386     } else if (rpmfdFromPyObject(obj, &fdo)) {
387         Py_BEGIN_ALLOW_THREADS;
388         h = headerRead(rpmfdGetFd(fdo), HEADER_MAGIC_YES);
389         Py_END_ALLOW_THREADS;
390         Py_XDECREF(fdo);
391     } else {
392         PyErr_SetString(PyExc_TypeError, "header, blob or file expected");
393         return NULL;
394     }
395
396     if (h == NULL) {
397         PyErr_SetString(pyrpmError, "bad header");
398         return NULL;
399     }
400     
401     return hdr_Wrap(subtype, h);
402 }
403
404 static void hdr_dealloc(hdrObject * s)
405 {
406     if (s->h) headerFree(s->h);
407     Py_TYPE(s)->tp_free((PyObject *)s);
408 }
409
410 static PyObject * hdr_iternext(hdrObject *s)
411 {
412     PyObject *res = NULL;
413     rpmTagVal tag;
414
415     if (s->hi == NULL) s->hi = headerInitIterator(s->h);
416
417     if ((tag = headerNextTag(s->hi)) != RPMTAG_NOT_FOUND) {
418         res = PyLong_FromLong(tag);
419     } else {
420         s->hi = headerFreeIterator(s->hi);
421     }
422     return res;
423 }
424
425 int utf8FromPyObject(PyObject *item, PyObject **str)
426 {
427     PyObject *res = NULL;
428     if (PyBytes_Check(item)) {
429         Py_XINCREF(item);
430         res = item;
431     } else if (PyUnicode_Check(item)) {
432         res = PyUnicode_AsUTF8String(item);
433     }
434     if (res == NULL) return 0;
435
436     *str = res;
437     return 1;
438 }
439
440 int tagNumFromPyObject (PyObject *item, rpmTagVal *tagp)
441 {
442     rpmTagVal tag = RPMTAG_NOT_FOUND;
443     PyObject *str = NULL;
444
445     if (PyLong_Check(item)) {
446         /* XXX we should probably validate tag numbers too */
447         tag = PyLong_AsLong(item);
448     } else if (utf8FromPyObject(item, &str)) {
449         tag = rpmTagGetValue(PyBytes_AsString(str));
450         Py_DECREF(str);
451     } else {
452         PyErr_SetString(PyExc_TypeError, "expected a string or integer");
453         return 0;
454     }
455     if (tag == RPMTAG_NOT_FOUND) {
456         PyErr_SetString(PyExc_ValueError, "unknown header tag");
457         return 0;
458     }
459
460     *tagp = tag;
461     return 1;
462 }
463
464 static PyObject * hdrGetTag(Header h, rpmTagVal tag)
465 {
466     PyObject *res = NULL;
467     struct rpmtd_s td;
468
469     (void) headerGet(h, tag, &td, HEADERGET_EXT);
470     if (rpmtdGetFlags(&td) & RPMTD_INVALID) {
471         PyErr_SetString(pyrpmError, "invalid header data");
472     } else {
473         /* rpmtd_AsPyobj() knows how to handle empty containers and all */
474         res = rpmtd_AsPyobj(&td);
475     }
476     rpmtdFreeData(&td);
477     return res;
478 }
479
480 static int validItem(rpmTagClass tclass, PyObject *item)
481 {
482     int rc;
483
484     switch (tclass) {
485     case RPM_NUMERIC_CLASS:
486         rc = (PyLong_Check(item) || PyLong_Check(item));
487         break;
488     case RPM_STRING_CLASS:
489         rc = (PyBytes_Check(item) || PyUnicode_Check(item));
490         break;
491     case RPM_BINARY_CLASS:
492         rc = PyBytes_Check(item);
493         break;
494     default:
495         rc = 0;
496         break;
497     }
498     return rc;
499 }
500
501 static int validData(rpmTagVal tag, rpmTagType type, rpmTagReturnType retype, PyObject *value)
502 {
503     rpmTagClass tclass = rpmTagGetClass(tag);
504     int valid = 1;
505     
506     if (retype == RPM_SCALAR_RETURN_TYPE) {
507         valid = validItem(tclass, value);
508     } else if (retype == RPM_ARRAY_RETURN_TYPE && PyList_Check(value)) {
509         /* python lists can contain arbitrary objects, validate each item */
510         Py_ssize_t len = PyList_Size(value);
511         if (len == 0)
512             valid = 0;
513         for (Py_ssize_t i = 0; i < len; i++) {
514             PyObject *item = PyList_GetItem(value, i);
515             if (!validItem(tclass, item)) {
516                 valid = 0;
517                 break;
518             }
519         }
520     } else {
521         valid = 0;
522     }
523     return valid;
524 }
525
526 static int hdrAppendItem(Header h, rpmTagVal tag, rpmTagType type, PyObject *item)
527 {
528     int rc = 0;
529
530     switch (type) {
531     case RPM_I18NSTRING_TYPE: /* XXX this needs to be handled separately */
532     case RPM_STRING_TYPE:
533     case RPM_STRING_ARRAY_TYPE: {
534         PyObject *str = NULL;
535         if (utf8FromPyObject(item, &str)) 
536             rc = headerPutString(h, tag, PyBytes_AsString(str));
537         Py_XDECREF(str);
538         } break;
539     case RPM_BIN_TYPE: {
540         uint8_t *val = (uint8_t *) PyBytes_AsString(item);
541         rpm_count_t len = PyBytes_Size(item);
542         rc = headerPutBin(h, tag, val, len);
543         } break;
544     case RPM_INT64_TYPE: {
545         uint64_t val = PyLong_AsUnsignedLongLongMask(item);
546         rc = headerPutUint64(h, tag, &val, 1);
547         } break;
548     case RPM_INT32_TYPE: {
549         uint32_t val = PyLong_AsUnsignedLongMask(item);
550         rc = headerPutUint32(h, tag, &val, 1);
551         } break;
552     case RPM_INT16_TYPE: {
553         uint16_t val = PyLong_AsUnsignedLongMask(item);
554         rc = headerPutUint16(h, tag, &val, 1);
555         } break;
556     case RPM_INT8_TYPE:
557     case RPM_CHAR_TYPE: {
558         uint8_t val = PyLong_AsUnsignedLongMask(item);
559         rc = headerPutUint8(h, tag, &val, 1);
560         } break;
561     default:
562         PyErr_SetString(PyExc_TypeError, "unhandled datatype");
563     }
564     return rc;
565 }
566
567 static int hdrPutTag(Header h, rpmTagVal tag, PyObject *value)
568 {
569     rpmTagType type = rpmTagGetTagType(tag);
570     rpmTagReturnType retype = rpmTagGetReturnType(tag);
571     int rc = 0;
572
573     /* XXX this isn't really right (i18n strings etc) but for now ... */
574     if (headerIsEntry(h, tag)) {
575         PyErr_SetString(PyExc_TypeError, "tag already exists");
576         return rc;
577     }
578
579     /* validate all data before trying to insert */
580     if (!validData(tag, type, retype, value)) { 
581         PyErr_SetString(PyExc_TypeError, "invalid type for tag");
582         return 0;
583     }
584
585     if (retype == RPM_SCALAR_RETURN_TYPE) {
586         rc = hdrAppendItem(h, tag, type, value);
587     } else if (retype == RPM_ARRAY_RETURN_TYPE && PyList_Check(value)) {
588         Py_ssize_t len = PyList_Size(value);
589         for (Py_ssize_t i = 0; i < len; i++) {
590             PyObject *item = PyList_GetItem(value, i);
591             rc = hdrAppendItem(h, tag, type, item);
592         }
593     } else {
594         PyErr_SetString(PyExc_RuntimeError, "can't happen, right?");
595     }
596
597     return rc;
598 }
599
600 static PyObject * hdr_subscript(hdrObject * s, PyObject * item)
601 {
602     rpmTagVal tag;
603
604     if (!tagNumFromPyObject(item, &tag)) return NULL;
605     return hdrGetTag(s->h, tag);
606 }
607
608 static int hdr_ass_subscript(hdrObject *s, PyObject *key, PyObject *value)
609 {
610     rpmTagVal tag;
611     if (!tagNumFromPyObject(key, &tag)) return -1;
612
613     if (value == NULL) {
614         /* XXX should failure raise key error? */
615         headerDel(s->h, tag);
616     } else if (!hdrPutTag(s->h, tag, value)) {
617         return -1;
618     }
619     return 0;
620 }
621
622 static PyObject * hdr_getattro(hdrObject * s, PyObject * n)
623 {
624     PyObject *res = PyObject_GenericGetAttr((PyObject *) s, n);
625     if (res == NULL) {
626         PyObject *type, *value, *traceback;
627         rpmTagVal tag;
628
629         /* Save and restore original exception if it's not a valid tag either */
630         PyErr_Fetch(&type, &value, &traceback);
631         if (tagNumFromPyObject(n, &tag)) {
632             Py_XDECREF(type);
633             Py_XDECREF(value);
634             Py_XDECREF(traceback);
635             res = hdrGetTag(s->h, tag);
636         } else {
637             PyErr_Restore(type, value, traceback);
638         }
639     }
640     return res;
641 }
642
643 static int hdr_setattro(PyObject * o, PyObject * n, PyObject * v)
644 {
645     return PyObject_GenericSetAttr(o, n, v);
646 }
647
648 static PyMappingMethods hdr_as_mapping = {
649         (lenfunc) 0,                    /* mp_length */
650         (binaryfunc) hdr_subscript,     /* mp_subscript */
651         (objobjargproc)hdr_ass_subscript,/* mp_ass_subscript */
652 };
653
654 static PySequenceMethods hdr_as_sequence = {
655     0,                          /* sq_length */
656     0,                          /* sq_concat */
657     0,                          /* sq_repeat */
658     0,                          /* sq_item */
659     0,                          /* sq_slice */
660     0,                          /* sq_ass_item */
661     0,                          /* sq_ass_slice */
662     (objobjproc)hdrContains,    /* sq_contains */
663     0,                          /* sq_inplace_concat */
664     0,                          /* sq_inplace_repeat */
665 };
666
667 static char hdr_doc[] =
668   "A header object represents an RPM package header.\n"
669   "\n"
670   "All RPM packages have headers that provide metadata for the package.\n"
671   "Header objects can be returned by database queries or loaded from a\n"
672   "binary package on disk.\n"
673   "\n"
674   "The ts.hdrFromFdno() function returns the package header from a\n"
675   "package on disk, verifying package signatures and digests of the\n"
676   "package while reading.\n"
677   "\n"
678   "Note: The older method rpm.headerFromPackage() which has been replaced\n"
679   "by ts.hdrFromFdno() used to return a (hdr, isSource) tuple.\n"
680   "\n"
681   "If you need to distinguish source/binary headers, do:\n"
682   "\n"
683   "     import os, rpm\n"
684   "\n"
685   "     ts = rpm.TransactionSet()\n"
686   "     fdno = os.open('/tmp/foo-1.0-1.i386.rpm', os.O_RDONLY)\n"
687   "     hdr = ts.hdrFromFdno(fdno)\n"
688   "     os.close(fdno)\n"
689   "     if hdr[rpm.RPMTAG_SOURCEPACKAGE]:\n"
690   "        print 'header is from a source package'\n"
691   "     else:\n"
692   "        print 'header is from a binary package'\n"
693   "\n"
694   "The Python interface to the header data is quite elegant.  It\n"
695   "presents the data in a dictionary form.  We'll take the header we\n"
696   "just loaded and access the data within it:\n"
697   "\n"
698   "     print hdr[rpm.RPMTAG_NAME]\n"
699   "     print hdr[rpm.RPMTAG_VERSION]\n"
700   "     print hdr[rpm.RPMTAG_RELEASE]\n"
701   "\n"
702   "in the case of our 'foo-1.0-1.i386.rpm' package, this code would\n"
703   "output:\n"
704   "     foo\n"
705   "     1.0\n"
706   "     1\n"
707   "\n"
708   "You make also access the header data by string name:\n"
709   "\n"
710   "     print hdr['name']\n"
711   "     print hdr['version']\n"
712   "     print hdr['release']\n"
713   "\n"
714   "This method of access is a teensy bit slower because the name must be\n"
715   "translated into the tag number dynamically. You also must make sure\n"
716   "the strings in header lookups don't get translated, or the lookups\n"
717   "will fail.\n";
718
719 PyTypeObject hdr_Type = {
720         PyVarObject_HEAD_INIT(&PyType_Type, 0)
721         "rpm.hdr",                      /* tp_name */
722         sizeof(hdrObject),              /* tp_size */
723         0,                              /* tp_itemsize */
724         (destructor) hdr_dealloc,       /* tp_dealloc */
725         0,                              /* tp_print */
726         (getattrfunc) 0,                /* tp_getattr */
727         0,                              /* tp_setattr */
728         0,                              /* tp_compare */
729         0,                              /* tp_repr */
730         0,                              /* tp_as_number */
731         &hdr_as_sequence,               /* tp_as_sequence */
732         &hdr_as_mapping,                /* tp_as_mapping */
733         hdr_hash,                       /* tp_hash */
734         0,                              /* tp_call */
735         0,                              /* tp_str */
736         (getattrofunc) hdr_getattro,    /* tp_getattro */
737         (setattrofunc) hdr_setattro,    /* tp_setattro */
738         0,                              /* tp_as_buffer */
739         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
740         hdr_doc,                        /* tp_doc */
741         0,                              /* tp_traverse */
742         0,                              /* tp_clear */
743         0,                              /* tp_richcompare */
744         0,                              /* tp_weaklistoffset */
745         PyObject_SelfIter,              /* tp_iter */
746         (iternextfunc)hdr_iternext,     /* tp_iternext */
747         hdr_methods,                    /* tp_methods */
748         0,                              /* tp_members */
749         0,                              /* tp_getset */
750         0,                              /* tp_base */
751         0,                              /* tp_dict */
752         0,                              /* tp_descr_get */
753         0,                              /* tp_descr_set */
754         0,                              /* tp_dictoffset */
755         0,                              /* tp_init */
756         0,                              /* tp_alloc */
757         hdr_new,                        /* tp_new */
758         0,                              /* tp_free */
759         0,                              /* tp_is_gc */
760 };
761
762 PyObject * hdr_Wrap(PyTypeObject *subtype, Header h)
763 {
764     hdrObject * hdr = (hdrObject *)subtype->tp_alloc(subtype, 0);
765     if (hdr == NULL) return NULL;
766     hdr->h = h;
767     return (PyObject *) hdr;
768 }
769
770 int hdrFromPyObject(PyObject *item, Header *hptr)
771 {
772     if (hdrObject_Check(item)) {
773         *hptr = ((hdrObject *) item)->h;
774         return 1;
775     } else {
776         PyErr_SetString(PyExc_TypeError, "header object expected");
777         return 0;
778     }
779 }
780
781 /**
782  * This assumes the order of list matches the order of the new headers, and
783  * throws an exception if that isn't true.
784  */
785 static int rpmMergeHeaders(PyObject * list, FD_t fd, int matchTag)
786 {
787     Header h;
788     HeaderIterator hi;
789     rpmTagVal newMatch, oldMatch;
790     hdrObject * hdr;
791     rpm_count_t count = 0;
792     int rc = 1; /* assume failure */
793     rpmtd td = rpmtdNew();
794
795     Py_BEGIN_ALLOW_THREADS
796     h = headerRead(fd, HEADER_MAGIC_YES);
797     Py_END_ALLOW_THREADS
798
799     while (h) {
800         if (!headerGet(h, matchTag, td, HEADERGET_MINMEM)) {
801             PyErr_SetString(pyrpmError, "match tag missing in new header");
802             goto exit;
803         }
804         newMatch = rpmtdTag(td);
805         rpmtdFreeData(td);
806
807         hdr = (hdrObject *) PyList_GetItem(list, count++);
808         if (!hdr) goto exit;
809
810         if (!headerGet(hdr->h, matchTag, td, HEADERGET_MINMEM)) {
811             PyErr_SetString(pyrpmError, "match tag missing in new header");
812             goto exit;
813         }
814         oldMatch = rpmtdTag(td);
815         rpmtdFreeData(td);
816
817         if (newMatch != oldMatch) {
818             PyErr_SetString(pyrpmError, "match tag mismatch");
819             goto exit;
820         }
821
822         for (hi = headerInitIterator(h); headerNext(hi, td); rpmtdFreeData(td))
823         {
824             /* could be dupes */
825             headerDel(hdr->h, rpmtdTag(td));
826             headerPut(hdr->h, td, HEADERPUT_DEFAULT);
827         }
828
829         headerFreeIterator(hi);
830         h = headerFree(h);
831
832         Py_BEGIN_ALLOW_THREADS
833         h = headerRead(fd, HEADER_MAGIC_YES);
834         Py_END_ALLOW_THREADS
835     }
836     rc = 0;
837
838 exit:
839     rpmtdFree(td);
840     return rc;
841 }
842
843 PyObject *
844 rpmMergeHeadersFromFD(PyObject * self, PyObject * args, PyObject * kwds)
845 {
846     FD_t fd;
847     int fileno;
848     PyObject * list;
849     int rc;
850     int matchTag;
851     char * kwlist[] = {"list", "fd", "matchTag", NULL};
852
853     if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oii", kwlist, &list,
854             &fileno, &matchTag))
855         return NULL;
856
857     if (!PyList_Check(list)) {
858         PyErr_SetString(PyExc_TypeError, "first parameter must be a list");
859         return NULL;
860     }
861
862     fd = fdDup(fileno);
863
864     rc = rpmMergeHeaders (list, fd, matchTag);
865     Fclose(fd);
866
867     if (rc) {
868         return NULL;
869     }
870
871     Py_RETURN_NONE;
872 }
873
874 PyObject * versionCompare (PyObject * self, PyObject * args, PyObject * kwds)
875 {
876     hdrObject * h1, * h2;
877     char * kwlist[] = {"version0", "version1", NULL};
878
879     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!", kwlist, &hdr_Type,
880             &h1, &hdr_Type, &h2))
881         return NULL;
882
883     return Py_BuildValue("i", rpmVersionCompare(h1->h, h2->h));
884 }
885
886 static int compare_values(const char *str1, const char *str2)
887 {
888     if (!str1 && !str2)
889         return 0;
890     else if (str1 && !str2)
891         return 1;
892     else if (!str1 && str2)
893         return -1;
894     return rpmvercmp(str1, str2);
895 }
896
897 PyObject * labelCompare (PyObject * self, PyObject * args)
898 {
899     const char *v1, *r1, *v2, *r2;
900     const char *e1, *e2;
901     int rc;
902
903     if (!PyArg_ParseTuple(args, "(zzz)(zzz)",
904                         &e1, &v1, &r1, &e2, &v2, &r2))
905         return NULL;
906
907     if (e1 == NULL)     e1 = "0";
908     if (e2 == NULL)     e2 = "0";
909
910     rc = compare_values(e1, e2);
911     if (!rc) {
912         rc = compare_values(v1, v2);
913         if (!rc)
914             rc = compare_values(r1, r2);
915     }
916     return Py_BuildValue("i", rc);
917 }
918