Add rpm_off_t type for file size types, use where spotted
[platform/upstream/rpm.git] / python / rpmts-py.c
1 /** \ingroup py_c
2  * \file python/rpmts-py.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmlib.h> /* rpmReadPackageFile, headerCheck */
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmpgp.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmbuild.h>
12 #include <rpm/idtx.h>
13
14 #include "header-py.h"
15 #include "rpmds-py.h"   /* XXX for rpmdsNew */
16 #include "rpmfi-py.h"   /* XXX for rpmfiNew */
17 #include "rpmmi-py.h"
18 #include "rpmps-py.h"
19 #include "rpmte-py.h"
20 #include "spec-py.h"
21
22 #include "rpmts-py.h"
23
24 #include "debug.h"
25
26 extern int _rpmts_debug;
27
28
29 /** \ingroup python
30  * \name Class: Rpmts
31  * \class Rpmts
32  * \brief A python rpm.ts object represents an RPM transaction set.
33  *
34  * The transaction set is the workhorse of RPM.  It performs the
35  * installation and upgrade of packages.  The rpm.ts object is
36  * instantiated by the TransactionSet function in the rpm module.
37  *
38  * The TransactionSet function takes two optional arguments. The first
39  * argument is the root path. The second is the verify signature disable flags,
40  * a set of the following bits:
41  *
42  * -    rpm.RPMVSF_NOHDRCHK     if set, don't check rpmdb headers
43  * -    rpm.RPMVSF_NEEDPAYLOAD  if not set, check header+payload (if possible)
44  * -    rpm.RPMVSF_NOSHA1HEADER if set, don't check header SHA1 digest
45  * -    rpm.RPMVSF_NODSAHEADER  if set, don't check header DSA signature
46  * -    rpm.RPMVSF_NOMD5        if set, don't check header+payload MD5 digest
47  * -    rpm.RPMVSF_NODSA        if set, don't check header+payload DSA signature
48  * -    rpm.RPMVSF_NORSA        if set, don't check header+payload RSA signature
49  *
50  * For convenience, there are the following masks:
51  * -    rpm._RPMVSF_NODIGESTS           if set, don't check digest(s).
52  * -    rpm._RPMVSF_NOSIGNATURES        if set, don't check signature(s).
53  *
54  * A rpm.ts object has the following methods:
55  *
56  * - addInstall(hdr,data,mode)  Add an install element to a transaction set.
57  * @param hdr   the header to be added
58  * @param data  user data that will be passed to the transaction callback
59  *              during transaction execution
60  * @param mode  optional argument that specifies if this package should
61  *              be installed ('i'), upgraded ('u'), or if it is just
62  *              available to the transaction when computing
63  *              dependencies but no action should be performed with it
64  *              ('a').
65  *
66  * - addErase(name) Add an erase element to a transaction set.
67  * @param name  the package name to be erased
68  *
69  * - check()    Perform a dependency check on the transaction set. After
70  *              headers have been added to a transaction set, a dependency
71  *              check can be performed to make sure that all package
72  *              dependencies are satisfied.
73  * @return      None If there are no unresolved dependencies
74  *              Otherwise a list of complex tuples is returned, one tuple per
75  *              unresolved dependency, with
76  * The format of the dependency tuple is:
77  *     ((packageName, packageVersion, packageRelease),
78  *      (reqName, reqVersion),
79  *      needsFlags,
80  *      suggestedPackage,
81  *      sense)
82  *     packageName, packageVersion, packageRelease are the name,
83  *     version, and release of the package that has the unresolved
84  *     dependency or conflict.
85  *     The reqName and reqVersion are the name and version of the
86  *     requirement or conflict.
87  *     The needsFlags is a bitfield that describes the versioned
88  *     nature of a requirement or conflict.  The constants
89  *     rpm.RPMSENSE_LESS, rpm.RPMSENSE_GREATER, and
90  *     rpm.RPMSENSE_EQUAL can be logical ANDed with the needsFlags
91  *     to get versioned dependency information.
92  *     suggestedPackage is a tuple if the dependency check was aware
93  *     of a package that solves this dependency problem when the
94  *     dependency check was run.  Packages that are added to the
95  *     transaction set as "available" are examined during the
96  *     dependency check as possible dependency solvers. The tuple
97  *     contains two values, (header, suggestedName).  These are set to
98  *     the header of the suggested package and its name, respectively.
99  *     If there is no known package to solve the dependency problem,
100  *     suggestedPackage is None.
101  *     The constants rpm.RPMDEP_SENSE_CONFLICTS and
102  *     rpm.RPMDEP_SENSE_REQUIRES are set to show a dependency as a
103  *     requirement or a conflict.
104  *
105  * - ts.order() Do a topological sort of added element relations.
106  * @return      None
107  *
108  * - ts.setFlags(transFlags) Set transaction set flags.
109  * @param transFlags - bit(s) to controll transaction operations. The
110  *              following values can be logically OR'ed together:
111  *      - rpm.RPMTRANS_FLAG_TEST - test mode, do not modify the RPM
112  *              database, change any files, or run any package scripts
113  *      - rpm.RPMTRANS_FLAG_BUILD_PROBS - only build a list of
114  *              problems encountered when attempting to run this transaction
115  *              set
116  *      - rpm.RPMTRANS_FLAG_NOSCRIPTS - do not execute package scripts
117  *      - rpm.RPMTRANS_FLAG_JUSTDB - only make changes to the rpm
118  *              database, do not modify files.
119  *      - rpm.RPMTRANS_FLAG_NOTRIGGERS - do not run trigger scripts
120  *      - rpm.RPMTRANS_FLAG_NODOCS - do not install files marked as %doc
121  *      - rpm.RPMTRANS_FLAG_ALLFILES - create all files, even if a
122  *              file is marked %config(missingok) and an upgrade is
123  *              being performed.
124  *      - rpm.RPMTRANS_FLAG_KEEPOBSOLETE - do not remove obsoleted
125  *              packages.
126  * @return      previous transFlags
127  *
128  * - ts.setProbFilter(ignoreSet) Set transaction set problem filter.
129  * @param problemSetFilter - control bit(s) to ignore classes of problems,
130  *              a logical or of one or more of the following bit(s):
131  *      - rpm.RPMPROB_FILTER_IGNOREOS -
132  *      - rpm.RPMPROB_FILTER_IGNOREARCH -
133  *      - rpm.RPMPROB_FILTER_REPLACEPKG -
134  *      - rpm.RPMPROB_FILTER_FORCERELOCATE -
135  *      - rpm.RPMPROB_FILTER_REPLACENEWFILES -
136  *      - rpm.RPMPROB_FILTER_REPLACEOLDFILES -
137  *      - rpm.RPMPROB_FILTER_OLDPACKAGE -
138  *      - rpm.RPMPROB_FILTER_DISKSPACE -
139  * @return      previous ignoreSet
140  *
141  * - ts.run(callback,data) Attempt to execute a transaction set.
142  *      After the transaction set has been populated with install/upgrade or
143  *      erase actions, the transaction set can be executed by invoking
144  *      the ts.run() method.
145  */
146
147 /** \ingroup py_c
148  */
149 struct rpmtsCallbackType_s {
150     PyObject * cb;
151     PyObject * data;
152     rpmtsObject * tso;
153     int pythonError;
154     PyThreadState *_save;
155 };
156
157 /** \ingroup py_c
158  */
159 static PyObject *
160 rpmts_Debug(rpmtsObject * s, PyObject * args, PyObject * kwds)
161 {
162     char * kwlist[] = {"debugLevel", NULL};
163
164     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Debug", kwlist,
165             &_rpmts_debug))
166         return NULL;
167
168 if (_rpmts_debug < 0)
169 fprintf(stderr, "*** rpmts_Debug(%p) ts %p\n", s, s->ts);
170
171     Py_INCREF(Py_None);
172     return Py_None;
173 }
174
175 #if 0
176 /** \ingroup py_c
177  * Add package to universe of possible packages to install in transaction set.
178  * @param ts            transaction set
179  * @param h             header
180  * @param key           package private data
181  */
182 static void rpmtsAddAvailableElement(rpmts ts, Header h,
183                 fnpyKey key)
184 {
185     int scareMem = 0;
186     rpmds provides = rpmdsNew(h, RPMTAG_PROVIDENAME, scareMem);
187     rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
188
189     /* XXX FIXME: return code RPMAL_NOMATCH is error */
190     (void) rpmalAdd(&ts->availablePackages, RPMAL_NOMATCH, key,
191                 provides, fi, rpmtsColor(ts));
192     fi = rpmfiFree(fi);
193     provides = rpmdsFree(provides);
194
195 if (_rpmts_debug < 0)
196 fprintf(stderr, "\tAddAvailable(%p) list %p\n", ts, ts->availablePackages);
197
198 }
199 #endif
200
201 /** \ingroup py_c
202  */
203 static PyObject *
204 rpmts_AddInstall(rpmtsObject * s, PyObject * args, PyObject * kwds)
205 {
206     hdrObject * h;
207     PyObject * key;
208     char * how = "u";   /* XXX default to upgrade element if missing */
209     int isUpgrade = 0;
210     char * kwlist[] = {"header", "key", "how", NULL};
211     int rc = 0;
212
213     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|s:AddInstall", kwlist,
214             &hdr_Type, &h, &key, &how))
215         return NULL;
216
217     {   PyObject * hObj = (PyObject *) h;
218         if (hObj->ob_type != &hdr_Type) {
219             PyErr_SetString(PyExc_TypeError, "bad type for header argument");
220             return NULL;
221         }
222     }
223
224 if (_rpmts_debug < 0 || (_rpmts_debug > 0 && *how != 'a'))
225 fprintf(stderr, "*** rpmts_AddInstall(%p,%p,%p,%s) ts %p\n", s, h, key, how, s->ts);
226
227     if (how && strcmp(how, "a") && strcmp(how, "u") && strcmp(how, "i")) {
228         PyErr_SetString(PyExc_TypeError, "how argument must be \"u\", \"a\", or \"i\"");
229         return NULL;
230     } else if (how && !strcmp(how, "u"))
231         isUpgrade = 1;
232
233     /*
234      * XXX resurrect when better available mechanism is, well, available.
235      * OTOH nothing appears to use it these days...
236      * Raise exception to catch out any callers while broken.
237      */
238     if (how && !strcmp(how, "a")) {
239 #ifdef DYING
240         rpmtsAddAvailableElement(s->ts, hdrGetHeader(h), key); 
241 #else
242         PyErr_SetString(pyrpmError, "available package mechanism currently broken");
243         return NULL;
244 #endif
245     } else
246         rc = rpmtsAddInstallElement(s->ts, hdrGetHeader(h), key, isUpgrade, NULL);
247     if (rc) {
248         PyErr_SetString(pyrpmError, "adding package to transaction failed");
249         return NULL;
250     }
251         
252
253     /* This should increment the usage count for me */
254     if (key)
255         PyList_Append(s->keyList, key);
256
257     Py_INCREF(Py_None);
258     return Py_None;
259 }
260
261 /** \ingroup py_c
262  * @todo Permit finer control (i.e. not just --allmatches) of deleted elments.
263  */
264 static PyObject *
265 rpmts_AddErase(rpmtsObject * s, PyObject * args, PyObject * kwds)
266 {
267     PyObject * o;
268     int count;
269     rpmdbMatchIterator mi;
270     char * kwlist[] = {"name", NULL};
271
272 if (_rpmts_debug)
273 fprintf(stderr, "*** rpmts_AddErase(%p) ts %p\n", s, s->ts);
274
275     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:AddErase", kwlist, &o))
276         return NULL;
277
278     if (PyString_Check(o)) {
279         char * name = PyString_AsString(o);
280
281         mi = rpmtsInitIterator(s->ts, RPMDBI_LABEL, name, 0);
282         count = rpmdbGetIteratorCount(mi);
283         if (count <= 0) {
284             mi = rpmdbFreeIterator(mi);
285             PyErr_SetString(pyrpmError, "package not installed");
286             return NULL;
287         } else { /* XXX: Note that we automatically choose to remove all matches */
288             Header h;
289             while ((h = rpmdbNextIterator(mi)) != NULL) {
290                 unsigned int recOffset = rpmdbGetIteratorOffset(mi);
291                 if (recOffset)
292                     rpmtsAddEraseElement(s->ts, h, recOffset);
293             }
294         }
295         mi = rpmdbFreeIterator(mi);
296     } else
297     if (PyInt_Check(o)) {
298         uint32_t instance = PyInt_AsLong(o);
299
300         mi = rpmtsInitIterator(s->ts, RPMDBI_PACKAGES, &instance, sizeof(instance));
301         if (instance == 0 || mi == NULL) {
302             mi = rpmdbFreeIterator(mi);
303             PyErr_SetString(pyrpmError, "package not installed");
304             return NULL;
305         } else {
306             Header h;
307             while ((h = rpmdbNextIterator(mi)) != NULL) {
308                 uint32_t recOffset = rpmdbGetIteratorOffset(mi);
309                 if (recOffset)
310                     rpmtsAddEraseElement(s->ts, h, recOffset);
311                 break;
312             }
313         }
314         mi = rpmdbFreeIterator(mi);
315     }
316
317     Py_INCREF(Py_None);
318     return Py_None;
319 }
320
321 /** \ingroup py_c
322  */
323 static int
324 rpmts_SolveCallback(rpmts ts, rpmds ds, const void * data)
325 {
326     struct rpmtsCallbackType_s * cbInfo = (struct rpmtsCallbackType_s *) data;
327     PyObject * args, * result;
328     int res = 1;
329
330 if (_rpmts_debug)
331 fprintf(stderr, "*** rpmts_SolveCallback(%p,%p,%p) \"%s\"\n", ts, ds, data, rpmdsDNEVR(ds));
332
333     if (cbInfo->tso == NULL) return res;
334     if (cbInfo->pythonError) return res;
335     if (cbInfo->cb == Py_None) return res;
336
337     PyEval_RestoreThread(cbInfo->_save);
338
339     args = Py_BuildValue("(Oissi)", cbInfo->tso,
340                 rpmdsTagN(ds), rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
341     result = PyEval_CallObject(cbInfo->cb, args);
342     Py_DECREF(args);
343
344     if (!result) {
345         cbInfo->pythonError = 1;
346     } else {
347         if (PyInt_Check(result))
348             res = PyInt_AsLong(result);
349         Py_DECREF(result);
350     }
351
352     cbInfo->_save = PyEval_SaveThread();
353
354     return res;
355 }
356
357 /** \ingroup py_c
358  */
359 static PyObject *
360 rpmts_Check(rpmtsObject * s, PyObject * args, PyObject * kwds)
361 {
362     rpmps ps;
363     rpmProblem p;
364     PyObject * list, * cf;
365     struct rpmtsCallbackType_s cbInfo;
366     int i;
367     int xx;
368     char * kwlist[] = {"callback", NULL};
369
370     memset(&cbInfo, 0, sizeof(cbInfo));
371     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:Check", kwlist,
372             &cbInfo.cb))
373         return NULL;
374
375     if (cbInfo.cb != NULL) {
376         if (!PyCallable_Check(cbInfo.cb)) {
377             PyErr_SetString(PyExc_TypeError, "expected a callable");
378             return NULL;
379         }
380         xx = rpmtsSetSolveCallback(s->ts, rpmts_SolveCallback, (void *)&cbInfo);
381     }
382
383 if (_rpmts_debug)
384 fprintf(stderr, "*** rpmts_Check(%p) ts %p cb %p\n", s, s->ts, cbInfo.cb);
385
386     cbInfo.tso = s;
387     cbInfo.pythonError = 0;
388     cbInfo._save = PyEval_SaveThread();
389
390 #ifdef DYING
391     /* XXX resurrect availablePackages one more time ... */
392     rpmalMakeIndex(s->ts->availablePackages);
393 #endif
394
395     xx = rpmtsCheck(s->ts);
396     ps = rpmtsProblems(s->ts);
397
398     if (cbInfo.cb)
399         xx = rpmtsSetSolveCallback(s->ts, rpmtsSolve, NULL);
400
401     PyEval_RestoreThread(cbInfo._save);
402
403     if (ps != NULL) {
404         list = PyList_New(0);
405         rpmpsi psi = rpmpsInitIterator(ps);
406
407         /* XXX TODO: rpmlib >= 4.0.3 can return multiple suggested keys. */
408         while ((i = rpmpsNextIterator(psi)) >= 0) {
409             const char * needsName;
410             char * byName, * byVersion, * byRelease, *byArch;
411             char * needsOP, * needsVersion;
412             rpmsenseFlags needsFlags, sense;
413             fnpyKey key;
414
415             p = rpmpsGetProblem(psi);
416
417             /* XXX autorelocated i386 on ia64, fix system-config-packages! */
418             if (rpmProblemGetType(p) == RPMPROB_BADRELOCATE)
419                 continue;
420
421             byName = strdup(rpmProblemGetPkgNEVR(p));
422             if ((byArch= strrchr(byName, '.')) != NULL)
423                 *byArch++ = '\0';
424             if ((byRelease = strrchr(byName, '-')) != NULL)
425                 *byRelease++ = '\0';
426             if ((byVersion = strrchr(byName, '-')) != NULL)
427                 *byVersion++ = '\0';
428
429             key = rpmProblemGetKey(p);
430
431             needsName = rpmProblemGetAltNEVR(p);
432             if (needsName[1] == ' ') {
433                 sense = (needsName[0] == 'C')
434                         ? RPMDEP_SENSE_CONFLICTS : RPMDEP_SENSE_REQUIRES;
435                 needsName += 2;
436             } else
437                 sense = RPMDEP_SENSE_REQUIRES;
438             if ((needsVersion = strrchr(needsName, ' ')) != NULL)
439                 *needsVersion++ = '\0';
440
441             needsFlags = 0;
442             if ((needsOP = strrchr(needsName, ' ')) != NULL) {
443                 for (*needsOP++ = '\0'; *needsOP != '\0'; needsOP++) {
444                     if (*needsOP == '<')        needsFlags |= RPMSENSE_LESS;
445                     else if (*needsOP == '>')   needsFlags |= RPMSENSE_GREATER;
446                     else if (*needsOP == '=')   needsFlags |= RPMSENSE_EQUAL;
447                 }
448             }
449
450             cf = Py_BuildValue("((sss)(ss)iOi)", byName, byVersion, byRelease,
451                                needsName, needsVersion, needsFlags,
452                                (key != NULL ? key : Py_None),
453                                sense);
454             PyList_Append(list, (PyObject *) cf);
455             Py_DECREF(cf);
456             free(byName);
457         }
458
459         psi = rpmpsFreeIterator(psi);
460         ps = rpmpsFree(ps);
461
462         return list;
463     }
464
465     Py_INCREF(Py_None);
466     return Py_None;
467 }
468
469 /** \ingroup py_c
470  */
471 static PyObject *
472 rpmts_Order(rpmtsObject * s)
473 {
474     int rc;
475
476 if (_rpmts_debug)
477 fprintf(stderr, "*** rpmts_Order(%p) ts %p\n", s, s->ts);
478
479     Py_BEGIN_ALLOW_THREADS
480     rc = rpmtsOrder(s->ts);
481     Py_END_ALLOW_THREADS
482
483     return Py_BuildValue("i", rc);
484 }
485
486 /** \ingroup py_c
487  */
488 static PyObject *
489 rpmts_Clean(rpmtsObject * s)
490 {
491 if (_rpmts_debug)
492 fprintf(stderr, "*** rpmts_Clean(%p) ts %p\n", s, s->ts);
493
494     rpmtsClean(s->ts);
495
496     Py_INCREF(Py_None);
497     return Py_None;
498 }
499
500 /** \ingroup py_c
501  */
502 static PyObject *
503 rpmts_IDTXload(rpmtsObject * s)
504 {
505     PyObject * result = NULL;
506     rpm_tag_t tag = RPMTAG_INSTALLTID;
507     IDTX idtx;
508
509 if (_rpmts_debug)
510 fprintf(stderr, "*** rpmts_IDTXload(%p) ts %p\n", s, s->ts);
511
512     Py_BEGIN_ALLOW_THREADS
513     idtx = IDTXload(s->ts, tag);
514     Py_END_ALLOW_THREADS
515
516     if (idtx == NULL || idtx->nidt <= 0) {
517         Py_INCREF(Py_None);
518         result = Py_None;
519     } else {
520         PyObject * tuple;
521         PyObject * ho;
522         IDT idt;
523         int i;
524
525         result = PyTuple_New(idtx->nidt);
526         for (i = 0; i < idtx->nidt; i++) {
527             idt = idtx->idt + i;
528             ho = (PyObject *) hdr_Wrap(idt->h);
529             tuple = Py_BuildValue("(iOi)", idt->val.u32, ho, idt->instance);
530             PyTuple_SET_ITEM(result,  i, tuple);
531             Py_DECREF(ho);
532         }
533     }
534
535     idtx = IDTXfree(idtx);
536
537     return result;
538 }
539
540 /** \ingroup py_c
541  */
542 static PyObject *
543 rpmts_IDTXglob(rpmtsObject * s)
544 {
545     PyObject * result = NULL;
546     rpm_tag_t tag = RPMTAG_REMOVETID;
547     char * globstr;
548     IDTX idtx;
549
550 if (_rpmts_debug)
551 fprintf(stderr, "*** rpmts_IDTXglob(%p) ts %p\n", s, s->ts);
552
553     Py_BEGIN_ALLOW_THREADS
554     globstr = rpmExpand("%{_repackage_dir}/*.rpm", NULL);
555     idtx = IDTXglob(s->ts, globstr, tag);
556     globstr = _free(globstr);
557     Py_END_ALLOW_THREADS
558
559     if (idtx == NULL || idtx->nidt <= 0) {
560         Py_INCREF(Py_None);
561         result = Py_None;
562     } else {
563         PyObject * tuple;
564         PyObject * ho;
565         IDT idt;
566         int i;
567
568         result = PyTuple_New(idtx->nidt);
569         for (i = 0; i < idtx->nidt; i++) {
570             idt = idtx->idt + i;
571             ho = (PyObject *) hdr_Wrap(idt->h);
572             tuple = Py_BuildValue("(iOs)", idt->val.u32, ho, idt->key);
573             PyTuple_SET_ITEM(result,  i, tuple);
574             Py_DECREF(ho);
575         }
576     }
577
578     idtx = IDTXfree(idtx);
579
580     return result;
581 }
582
583 /** \ingroup py_c
584  */
585 static PyObject *
586 rpmts_Rollback(rpmtsObject * s, PyObject * args, PyObject * kwds)
587 {
588     struct rpmInstallArguments_s * ia = alloca(sizeof(*ia));
589     rpmtransFlags transFlags;
590     const char ** av = NULL;
591     uint32_t rbtid;
592     int rc;
593     char * kwlist[] = {"transactionId", NULL};
594
595 if (_rpmts_debug)
596 fprintf(stderr, "*** rpmts_Rollback(%p) ts %p\n", s, s->ts);
597
598     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Rollback", kwlist, &rbtid))
599         return NULL;
600
601     Py_BEGIN_ALLOW_THREADS
602     memset(ia, 0, sizeof(*ia));
603     ia->qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE|VERIFY_HDRCHK);
604     ia->transFlags |= (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL);
605     ia->transFlags |= RPMTRANS_FLAG_NOMD5;
606     ia->installInterfaceFlags = (INSTALL_UPGRADE|INSTALL_FRESHEN|INSTALL_INSTALL);
607     ia->rbtid = rbtid;
608     ia->relocations = NULL;
609     ia->probFilter |= RPMPROB_FILTER_OLDPACKAGE;
610
611     transFlags = rpmtsSetFlags(s->ts, ia->transFlags);
612     rc = rpmRollback(s->ts, ia, av);
613     transFlags = rpmtsSetFlags(s->ts, transFlags);
614     Py_END_ALLOW_THREADS
615
616     return Py_BuildValue("i", rc);
617 }
618
619 /** \ingroup py_c
620  */
621 static PyObject *
622 rpmts_OpenDB(rpmtsObject * s)
623 {
624     int dbmode;
625
626 if (_rpmts_debug)
627 fprintf(stderr, "*** rpmts_OpenDB(%p) ts %p\n", s, s->ts);
628
629     dbmode = rpmtsGetDBMode(s->ts);
630     if (dbmode == -1)
631         dbmode = O_RDONLY;
632
633     return Py_BuildValue("i", rpmtsOpenDB(s->ts, dbmode));
634 }
635
636 /** \ingroup py_c
637  */
638 static PyObject *
639 rpmts_CloseDB(rpmtsObject * s)
640 {
641     int rc;
642
643 if (_rpmts_debug)
644 fprintf(stderr, "*** rpmts_CloseDB(%p) ts %p\n", s, s->ts);
645
646     rc = rpmtsCloseDB(s->ts);
647     rpmtsSetDBMode(s->ts, -1);  /* XXX disable lazy opens */
648
649     return Py_BuildValue("i", rc);
650 }
651
652 /** \ingroup py_c
653  */
654 static PyObject *
655 rpmts_InitDB(rpmtsObject * s)
656 {
657     int rc;
658
659 if (_rpmts_debug)
660 fprintf(stderr, "*** rpmts_InitDB(%p) ts %p\n", s, s->ts);
661
662     rc = rpmtsInitDB(s->ts, O_RDONLY);
663     if (rc == 0)
664         rc = rpmtsCloseDB(s->ts);
665
666     return Py_BuildValue("i", rc);
667 }
668
669 /** \ingroup py_c
670  */
671 static PyObject *
672 rpmts_RebuildDB(rpmtsObject * s)
673 {
674     int rc;
675
676 if (_rpmts_debug)
677 fprintf(stderr, "*** rpmts_RebuildDB(%p) ts %p\n", s, s->ts);
678
679     Py_BEGIN_ALLOW_THREADS
680     rc = rpmtsRebuildDB(s->ts);
681     Py_END_ALLOW_THREADS
682
683     return Py_BuildValue("i", rc);
684 }
685
686 /** \ingroup py_c
687  */
688 static PyObject *
689 rpmts_VerifyDB(rpmtsObject * s)
690 {
691     int rc;
692
693 if (_rpmts_debug)
694 fprintf(stderr, "*** rpmts_VerifyDB(%p) ts %p\n", s, s->ts);
695
696     Py_BEGIN_ALLOW_THREADS
697     rc = rpmtsVerifyDB(s->ts);
698     Py_END_ALLOW_THREADS
699
700     return Py_BuildValue("i", rc);
701 }
702
703 /** \ingroup py_c
704  */
705 static PyObject *
706 rpmts_HdrFromFdno(rpmtsObject * s, PyObject * args, PyObject * kwds)
707 {
708     PyObject * result = NULL;
709     Header h;
710     FD_t fd;
711     int fdno;
712     rpmRC rpmrc;
713     char * kwlist[] = {"fd", NULL};
714
715     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:HdrFromFdno", kwlist,
716             &fdno))
717         return NULL;
718
719     fd = fdDup(fdno);
720     rpmrc = rpmReadPackageFile(s->ts, fd, "rpmts_HdrFromFdno", &h);
721     Fclose(fd);
722
723 if (_rpmts_debug)
724 fprintf(stderr, "*** rpmts_HdrFromFdno(%p) ts %p rc %d\n", s, s->ts, rpmrc);
725
726     switch (rpmrc) {
727     case RPMRC_OK:
728         if (h)
729             result = Py_BuildValue("N", hdr_Wrap(h));
730         h = headerFree(h);      /* XXX ref held by result */
731         break;
732
733     case RPMRC_NOKEY:
734         PyErr_SetString(pyrpmError, "public key not available");
735         break;
736
737     case RPMRC_NOTTRUSTED:
738         PyErr_SetString(pyrpmError, "public key not trusted");
739         break;
740
741     case RPMRC_NOTFOUND:
742     case RPMRC_FAIL:
743     default:
744         PyErr_SetString(pyrpmError, "error reading package header");
745         break;
746     }
747
748     return result;
749 }
750
751 /** \ingroup py_c
752  */
753 static PyObject *
754 rpmts_HdrCheck(rpmtsObject * s, PyObject * args, PyObject * kwds)
755 {
756     PyObject * blob;
757     PyObject * result = NULL;
758     char * msg = NULL;
759     const void * uh;
760     int uc;
761     rpmRC rpmrc;
762     char * kwlist[] = {"headers", NULL};
763
764 if (_rpmts_debug)
765 fprintf(stderr, "*** rpmts_HdrCheck(%p) ts %p\n", s, s->ts);
766
767     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:HdrCheck", kwlist, &blob))
768         return NULL;
769
770     if (blob == Py_None) {
771         Py_INCREF(Py_None);
772         return Py_None;
773     }
774     if (!PyString_Check(blob)) {
775         PyErr_SetString(pyrpmError, "hdrCheck takes a string of octets");
776         return result;
777     }
778     uh = PyString_AsString(blob);
779     uc = PyString_Size(blob);
780
781     rpmrc = headerCheck(s->ts, uh, uc, &msg);
782
783     switch (rpmrc) {
784     case RPMRC_OK:
785         Py_INCREF(Py_None);
786         result = Py_None;
787         break;
788
789     case RPMRC_NOKEY:
790         PyErr_SetString(pyrpmError, "public key not availaiable");
791         break;
792
793     case RPMRC_NOTTRUSTED:
794         PyErr_SetString(pyrpmError, "public key not trusted");
795         break;
796
797     case RPMRC_FAIL:
798     default:
799         PyErr_SetString(pyrpmError, msg);
800         break;
801     }
802     msg = _free(msg);
803
804     return result;
805 }
806
807 /** \ingroup py_c
808  */
809 static PyObject *
810 rpmts_SetVSFlags(rpmtsObject * s, PyObject * args, PyObject * kwds)
811 {
812     rpmVSFlags vsflags;
813     char * kwlist[] = {"flags", NULL};
814
815 if (_rpmts_debug)
816 fprintf(stderr, "*** rpmts_SetVSFlags(%p) ts %p\n", s, s->ts);
817
818     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetVSFlags", kwlist,
819             &vsflags))
820         return NULL;
821
822     /* XXX FIXME: value check on vsflags, or build pure python object 
823      * for it, and require an object of that type */
824
825     return Py_BuildValue("i", rpmtsSetVSFlags(s->ts, vsflags));
826 }
827
828 /** \ingroup py_c
829  */
830 static PyObject *
831 rpmts_GetVSFlags(rpmtsObject * s)
832 {
833     return Py_BuildValue("i", rpmtsVSFlags(s->ts));
834 }
835
836 /** \ingroup py_c
837  */
838 static PyObject *
839 rpmts_SetColor(rpmtsObject * s, PyObject * args, PyObject * kwds)
840 {
841     uint32_t tscolor;
842     char * kwlist[] = {"color", NULL};
843
844 if (_rpmts_debug)
845 fprintf(stderr, "*** rpmts_SetColor(%p) ts %p\n", s, s->ts);
846
847     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Color", kwlist, &tscolor))
848         return NULL;
849
850     /* XXX FIXME: value check on tscolor, or build pure python object
851      * for it, and require an object of that type */
852
853     return Py_BuildValue("i", rpmtsSetColor(s->ts, tscolor));
854 }
855
856 /** \ingroup py_c
857  */
858 static PyObject *
859 rpmts_PgpPrtPkts(rpmtsObject * s, PyObject * args, PyObject * kwds)
860 {
861     PyObject * blob;
862     unsigned char * pkt;
863     unsigned int pktlen;
864     int rc;
865     char * kwlist[] = {"octets", NULL};
866
867 if (_rpmts_debug)
868 fprintf(stderr, "*** rpmts_PgpPrtPkts(%p) ts %p\n", s, s->ts);
869
870     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PgpPrtPkts", kwlist, &blob))
871         return NULL;
872
873     if (blob == Py_None) {
874         Py_INCREF(Py_None);
875         return Py_None;
876     }
877     if (!PyString_Check(blob)) {
878         PyErr_SetString(pyrpmError, "pgpPrtPkts takes a string of octets");
879         return NULL;
880     }
881     pkt = (unsigned char *)PyString_AsString(blob);
882     pktlen = PyString_Size(blob);
883
884     rc = pgpPrtPkts(pkt, pktlen, NULL, 1);
885
886     return Py_BuildValue("i", rc);
887 }
888
889 /** \ingroup py_c
890  */
891 static PyObject *
892 rpmts_PgpImportPubkey(rpmtsObject * s, PyObject * args, PyObject * kwds)
893 {
894     PyObject * blob;
895     unsigned char * pkt;
896     unsigned int pktlen;
897     int rc;
898     char * kwlist[] = {"pubkey", NULL};
899
900 if (_rpmts_debug)
901 fprintf(stderr, "*** rpmts_PgpImportPubkey(%p) ts %p\n", s, s->ts);
902
903     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PgpImportPubkey",
904             kwlist, &blob))
905         return NULL;
906
907     if (blob == Py_None) {
908         Py_INCREF(Py_None);
909         return Py_None;
910     }
911     if (!PyString_Check(blob)) {
912         PyErr_SetString(pyrpmError, "PgpImportPubkey takes a string of octets");
913         return NULL;
914     }
915     pkt = (unsigned char *)PyString_AsString(blob);
916     pktlen = PyString_Size(blob);
917
918     rc = rpmtsImportPubkey(s->ts, pkt, pktlen);
919
920     return Py_BuildValue("i", rc);
921 }
922
923 /** \ingroup py_c
924  */
925 static PyObject *
926 rpmts_GetKeys(rpmtsObject * s)
927 {
928     const void **data = NULL;
929     int num, i;
930     PyObject *tuple;
931
932 if (_rpmts_debug)
933 fprintf(stderr, "*** rpmts_GetKeys(%p) ts %p\n", s, s->ts);
934
935     rpmtsGetKeys(s->ts, &data, &num);
936     if (data == NULL || num <= 0) {
937         data = _free(data);
938         Py_INCREF(Py_None);
939         return Py_None;
940     }
941
942     tuple = PyTuple_New(num);
943
944     for (i = 0; i < num; i++) {
945         PyObject *obj;
946         obj = (data[i] ? (PyObject *) data[i] : Py_None);
947         Py_INCREF(obj);
948         PyTuple_SetItem(tuple, i, obj);
949     }
950
951     data = _free(data);
952
953     return tuple;
954 }
955
956 /** \ingroup py_c
957  */
958 static void *
959 rpmtsCallback(const void * hd, const rpmCallbackType what,
960                          const rpm_off_t amount, const rpm_off_t total,
961                          const void * pkgKey, rpmCallbackData data)
962 {
963     Header h = (Header) hd;
964     struct rpmtsCallbackType_s * cbInfo = data;
965     PyObject * pkgObj = (PyObject *) pkgKey;
966     PyObject * args, * result;
967     static FD_t fd;
968
969     if (cbInfo->pythonError) return NULL;
970     if (cbInfo->cb == Py_None) return NULL;
971
972     /* Synthesize a python object for callback (if necessary). */
973     if (pkgObj == NULL) {
974         if (h) {
975             const char * n = NULL;
976             (void) headerNVR(h, &n, NULL, NULL);
977             pkgObj = Py_BuildValue("s", n);
978         } else {
979             pkgObj = Py_None;
980             Py_INCREF(pkgObj);
981         }
982     } else
983         Py_INCREF(pkgObj);
984
985     PyEval_RestoreThread(cbInfo->_save);
986
987     args = Py_BuildValue("(illOO)", what, amount, total, pkgObj, cbInfo->data);
988     result = PyEval_CallObject(cbInfo->cb, args);
989     Py_DECREF(args);
990     Py_DECREF(pkgObj);
991
992     if (!result) {
993         cbInfo->pythonError = 1;
994         cbInfo->_save = PyEval_SaveThread();
995         return NULL;
996     }
997
998     if (what == RPMCALLBACK_INST_OPEN_FILE) {
999         int fdno;
1000
1001         if (!PyArg_Parse(result, "i", &fdno)) {
1002             cbInfo->pythonError = 1;
1003             cbInfo->_save = PyEval_SaveThread();
1004             return NULL;
1005         }
1006         Py_DECREF(result);
1007         cbInfo->_save = PyEval_SaveThread();
1008
1009         fd = fdDup(fdno);
1010 if (_rpmts_debug)
1011 fprintf(stderr, "\t%p = fdDup(%d)\n", fd, fdno);
1012
1013         fcntl(Fileno(fd), F_SETFD, FD_CLOEXEC);
1014
1015         return fd;
1016     } else
1017     if (what == RPMCALLBACK_INST_CLOSE_FILE) {
1018 if (_rpmts_debug)
1019 fprintf(stderr, "\tFclose(%p)\n", fd);
1020         Fclose (fd);
1021     } else {
1022 if (_rpmts_debug)
1023 fprintf(stderr, "\t%ld:%ld key %p\n", amount, total, pkgKey);
1024     }
1025
1026     Py_DECREF(result);
1027     cbInfo->_save = PyEval_SaveThread();
1028
1029     return NULL;
1030 }
1031
1032 /** \ingroup py_c
1033  */
1034 static PyObject *
1035 rpmts_SetFlags(rpmtsObject * s, PyObject * args, PyObject * kwds)
1036 {
1037     rpmtransFlags transFlags = 0;
1038     char * kwlist[] = {"flags", NULL};
1039
1040     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetFlags", kwlist,
1041             &transFlags))
1042         return NULL;
1043
1044 if (_rpmts_debug)
1045 fprintf(stderr, "*** rpmts_SetFlags(%p) ts %p transFlags %x\n", s, s->ts, transFlags);
1046
1047     /* XXX FIXME: value check on flags, or build pure python object 
1048      * for it, and require an object of that type */
1049
1050     return Py_BuildValue("i", rpmtsSetFlags(s->ts, transFlags));
1051 }
1052
1053 /** \ingroup py_c
1054  */
1055 static PyObject *
1056 rpmts_SetProbFilter(rpmtsObject * s, PyObject * args, PyObject * kwds)
1057 {
1058     rpmprobFilterFlags ignoreSet = 0;
1059     rpmprobFilterFlags oignoreSet;
1060     char * kwlist[] = {"ignoreSet", NULL};
1061
1062     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:ProbFilter", kwlist,
1063             &ignoreSet))
1064         return NULL;
1065
1066 if (_rpmts_debug)
1067 fprintf(stderr, "*** rpmts_SetProbFilter(%p) ts %p ignoreSet %x\n", s, s->ts, ignoreSet);
1068
1069     oignoreSet = s->ignoreSet;
1070     s->ignoreSet = ignoreSet;
1071
1072     return Py_BuildValue("i", oignoreSet);
1073 }
1074
1075 /** \ingroup py_c
1076  */
1077 static rpmpsObject *
1078 rpmts_Problems(rpmtsObject * s)
1079 {
1080
1081 if (_rpmts_debug)
1082 fprintf(stderr, "*** rpmts_Problems(%p) ts %p\n", s, s->ts);
1083
1084     return rpmps_Wrap( rpmtsProblems(s->ts) );
1085 }
1086
1087 /** \ingroup py_c
1088  */
1089 static PyObject *
1090 rpmts_Run(rpmtsObject * s, PyObject * args, PyObject * kwds)
1091 {
1092     int rc;
1093     PyObject * list;
1094     rpmps ps;
1095     rpmpsi psi;
1096     struct rpmtsCallbackType_s cbInfo;
1097     char * kwlist[] = {"callback", "data", NULL};
1098
1099     if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:Run", kwlist,
1100             &cbInfo.cb, &cbInfo.data))
1101         return NULL;
1102
1103     cbInfo.tso = s;
1104     cbInfo.pythonError = 0;
1105     cbInfo._save = PyEval_SaveThread();
1106
1107     if (cbInfo.cb != NULL) {
1108         if (!PyCallable_Check(cbInfo.cb)) {
1109             PyErr_SetString(PyExc_TypeError, "expected a callable");
1110             return NULL;
1111         }
1112         (void) rpmtsSetNotifyCallback(s->ts, rpmtsCallback, (void *) &cbInfo);
1113     }
1114
1115 if (_rpmts_debug)
1116 fprintf(stderr, "*** rpmts_Run(%p) ts %p ignore %x\n", s, s->ts, s->ignoreSet);
1117
1118     rc = rpmtsRun(s->ts, NULL, s->ignoreSet);
1119     ps = rpmtsProblems(s->ts);
1120
1121     if (cbInfo.cb)
1122         (void) rpmtsSetNotifyCallback(s->ts, NULL, NULL);
1123
1124     PyEval_RestoreThread(cbInfo._save);
1125
1126     if (cbInfo.pythonError) {
1127         ps = rpmpsFree(ps);
1128         return NULL;
1129     }
1130
1131     if (rc < 0) {
1132         list = PyList_New(0);
1133         return list;
1134     } else if (!rc) {
1135         Py_INCREF(Py_None);
1136         return Py_None;
1137     }
1138
1139     list = PyList_New(0);
1140     psi = rpmpsInitIterator(ps);
1141     while (rpmpsNextIterator(psi) >= 0) {
1142         rpmProblem p = rpmpsGetProblem(psi);
1143         char * ps = rpmProblemString(p);
1144         PyObject * prob = Py_BuildValue("s(isN)", ps,
1145                              rpmProblemGetType(p),
1146                              rpmProblemGetStr(p),
1147                              PyLong_FromLongLong(rpmProblemGetLong(p)));
1148         PyList_Append(list, prob);
1149         free(ps);
1150         Py_DECREF(prob);
1151     }
1152
1153     psi = rpmpsFreeIterator(psi);
1154     ps = rpmpsFree(ps);
1155
1156     return list;
1157 }
1158
1159 #if Py_TPFLAGS_HAVE_ITER
1160 static PyObject *
1161 rpmts_iter(rpmtsObject * s)
1162 {
1163 if (_rpmts_debug)
1164 fprintf(stderr, "*** rpmts_iter(%p) ts %p\n", s, s->ts);
1165
1166     Py_INCREF(s);
1167     return (PyObject *)s;
1168 }
1169 #endif
1170
1171 /**
1172  * @todo Add TR_ADDED filter to iterator.
1173  */
1174 static PyObject *
1175 rpmts_iternext(rpmtsObject * s)
1176 {
1177     PyObject * result = NULL;
1178     rpmte te;
1179
1180 if (_rpmts_debug)
1181 fprintf(stderr, "*** rpmts_iternext(%p) ts %p tsi %p %d\n", s, s->ts, s->tsi, s->tsiFilter);
1182
1183     /* Reset iterator on 1st entry. */
1184     if (s->tsi == NULL) {
1185         s->tsi = rpmtsiInit(s->ts);
1186         if (s->tsi == NULL)
1187             return NULL;
1188         s->tsiFilter = 0;
1189     }
1190
1191     te = rpmtsiNext(s->tsi, s->tsiFilter);
1192     if (te != NULL) {
1193         result = (PyObject *) rpmte_Wrap(te);
1194     } else {
1195         s->tsi = rpmtsiFree(s->tsi);
1196         s->tsiFilter = 0;
1197     }
1198
1199     return result;
1200 }
1201
1202 /**
1203  * @todo Add TR_ADDED filter to iterator.
1204  */
1205 static PyObject *
1206 rpmts_Next(rpmtsObject * s)
1207 {
1208     PyObject * result;
1209
1210 if (_rpmts_debug)
1211 fprintf(stderr, "*** rpmts_Next(%p) ts %p\n", s, s->ts);
1212
1213     result = rpmts_iternext(s);
1214
1215     if (result == NULL) {
1216         Py_INCREF(Py_None);
1217         return Py_None;
1218     }
1219
1220     return result;
1221 }
1222
1223 /**
1224  */
1225 static specObject *
1226 spec_Parse(rpmtsObject * s, PyObject * args, PyObject * kwds)
1227 {
1228     const char * specfile;
1229     rpmSpec spec;
1230     char * buildRoot = NULL;
1231     int recursing = 0;
1232     char * passPhrase = "";
1233     char *cookie = NULL;
1234     int anyarch = 1;
1235     int force = 1;
1236     char * kwlist[] = {"specfile", NULL};
1237
1238     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:Parse", kwlist, &specfile))
1239         return NULL;
1240
1241     if (parseSpec(s->ts, specfile,"/", buildRoot,recursing, passPhrase,
1242              cookie, anyarch, force)!=0) {
1243              PyErr_SetString(pyrpmError, "can't parse specfile\n");
1244                      return NULL;
1245    }
1246
1247     spec = rpmtsSpec(s->ts);
1248     return spec_Wrap(spec);
1249 }
1250
1251 /**
1252  */
1253 static rpmmiObject *
1254 rpmts_Match(rpmtsObject * s, PyObject * args, PyObject * kwds)
1255 {
1256     PyObject *TagN = NULL;
1257     PyObject *Key = NULL;
1258     char *key = NULL;
1259 /* XXX lkey *must* be a 32 bit integer, int "works" on all known platforms. */
1260     int lkey = 0;
1261     int len = 0;
1262     rpm_tag_t tag = RPMDBI_PACKAGES;
1263     char * kwlist[] = {"tagNumber", "key", NULL};
1264
1265 if (_rpmts_debug)
1266 fprintf(stderr, "*** rpmts_Match(%p) ts %p\n", s, s->ts);
1267
1268     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:Match", kwlist,
1269             &TagN, &Key))
1270         return NULL;
1271
1272     if (TagN && (tag = tagNumFromPyObject (TagN)) == -1) {
1273         PyErr_SetString(PyExc_TypeError, "unknown tag type");
1274         return NULL;
1275     }
1276
1277     if (Key) {
1278         if (PyString_Check(Key) || PyUnicode_Check(Key)) {
1279             key = PyString_AsString(Key);
1280             len = PyString_Size(Key);
1281         } else if (PyInt_Check(Key)) {
1282             lkey = PyInt_AsLong(Key);
1283             key = (char *)&lkey;
1284             len = sizeof(lkey);
1285         } else {
1286             PyErr_SetString(PyExc_TypeError, "unknown key type");
1287             return NULL;
1288         }
1289     }
1290
1291     /* XXX If not already opened, open the database O_RDONLY now. */
1292     /* XXX FIXME: lazy default rdonly open also done by rpmtsInitIterator(). */
1293     if (rpmtsGetRdb(s->ts) == NULL) {
1294         int rc = rpmtsOpenDB(s->ts, O_RDONLY);
1295         if (rc || rpmtsGetRdb(s->ts) == NULL) {
1296             PyErr_SetString(PyExc_TypeError, "rpmdb open failed");
1297             return NULL;
1298         }
1299     }
1300
1301     return rpmmi_Wrap( rpmtsInitIterator(s->ts, tag, key, len), (PyObject*)s);
1302 }
1303
1304 /** \ingroup py_c
1305  */
1306 static struct PyMethodDef rpmts_methods[] = {
1307  {"Debug",      (PyCFunction)rpmts_Debug,       METH_VARARGS|METH_KEYWORDS,
1308         NULL},
1309
1310  {"addInstall", (PyCFunction) rpmts_AddInstall, METH_VARARGS|METH_KEYWORDS,
1311         NULL },
1312  {"addErase",   (PyCFunction) rpmts_AddErase,   METH_VARARGS|METH_KEYWORDS,
1313         NULL },
1314  {"check",      (PyCFunction) rpmts_Check,      METH_VARARGS|METH_KEYWORDS,
1315         NULL },
1316  {"order",      (PyCFunction) rpmts_Order,      METH_NOARGS,
1317         NULL },
1318  {"setFlags",   (PyCFunction) rpmts_SetFlags,   METH_VARARGS|METH_KEYWORDS,
1319 "ts.setFlags(transFlags) -> previous transFlags\n\
1320 - Set control bit(s) for executing ts.run().\n\
1321   Note: This method replaces the 1st argument to the old ts.run()\n" },
1322  {"setProbFilter",      (PyCFunction) rpmts_SetProbFilter,      METH_VARARGS|METH_KEYWORDS,
1323 "ts.setProbFilter(ignoreSet) -> previous ignoreSet\n\
1324 - Set control bit(s) for ignoring problems found by ts.run().\n\
1325   Note: This method replaces the 2nd argument to the old ts.run()\n" },
1326  {"problems",   (PyCFunction) rpmts_Problems,   METH_NOARGS,
1327 "ts.problems() -> ps\n\
1328 - Return current problem set.\n" },
1329  {"run",        (PyCFunction) rpmts_Run,        METH_VARARGS|METH_KEYWORDS,
1330 "ts.run(callback, data) -> (problems)\n\
1331 - Run a transaction set, returning list of problems found.\n\
1332   Note: The callback may not be None.\n" },
1333  {"clean",      (PyCFunction) rpmts_Clean,      METH_NOARGS,
1334         NULL },
1335  {"IDTXload",   (PyCFunction) rpmts_IDTXload,   METH_NOARGS,
1336 "ts.IDTXload() -> ((tid,hdr,instance)+)\n\
1337 - Return list of installed packages reverse sorted by transaction id.\n" },
1338  {"IDTXglob",   (PyCFunction) rpmts_IDTXglob,   METH_NOARGS,
1339 "ts.IDTXglob() -> ((tid,hdr,instance)+)\n\
1340 - Return list of removed packages reverse sorted by transaction id.\n" },
1341  {"rollback",   (PyCFunction) rpmts_Rollback,   METH_VARARGS|METH_KEYWORDS,
1342         NULL },
1343  {"openDB",     (PyCFunction) rpmts_OpenDB,     METH_NOARGS,
1344 "ts.openDB() -> None\n\
1345 - Open the default transaction rpmdb.\n\
1346   Note: The transaction rpmdb is lazily opened, so ts.openDB() is seldom needed.\n" },
1347  {"closeDB",    (PyCFunction) rpmts_CloseDB,    METH_NOARGS,
1348 "ts.closeDB() -> None\n\
1349 - Close the default transaction rpmdb.\n\
1350   Note: ts.closeDB() disables lazy opens, and should hardly ever be used.\n" },
1351  {"initDB",     (PyCFunction) rpmts_InitDB,     METH_NOARGS,
1352 "ts.initDB() -> None\n\
1353 - Initialize the default transaction rpmdb.\n\
1354  Note: ts.initDB() is seldom needed anymore.\n" },
1355  {"rebuildDB",  (PyCFunction) rpmts_RebuildDB,  METH_NOARGS,
1356 "ts.rebuildDB() -> None\n\
1357 - Rebuild the default transaction rpmdb.\n" },
1358  {"verifyDB",   (PyCFunction) rpmts_VerifyDB,   METH_NOARGS,
1359 "ts.verifyDB() -> None\n\
1360 - Verify the default transaction rpmdb.\n" },
1361  {"hdrFromFdno",(PyCFunction) rpmts_HdrFromFdno,METH_VARARGS|METH_KEYWORDS,
1362 "ts.hdrFromFdno(fdno) -> hdr\n\
1363 - Read a package header from a file descriptor.\n" },
1364  {"hdrCheck",   (PyCFunction) rpmts_HdrCheck,   METH_VARARGS|METH_KEYWORDS,
1365         NULL },
1366  {"setVSFlags",(PyCFunction) rpmts_SetVSFlags,  METH_VARARGS|METH_KEYWORDS,
1367 "ts.setVSFlags(vsflags) -> ovsflags\n\
1368 - Set signature verification flags. Values for vsflags are:\n\
1369     rpm.RPMVSF_NOHDRCHK      if set, don't check rpmdb headers\n\
1370     rpm.RPMVSF_NEEDPAYLOAD   if not set, check header+payload (if possible)\n\
1371     rpm.RPMVSF_NOSHA1HEADER  if set, don't check header SHA1 digest\n\
1372     rpm.RPMVSF_NODSAHEADER   if set, don't check header DSA signature\n\
1373     rpm.RPMVSF_NOMD5         if set, don't check header+payload MD5 digest\n\
1374     rpm.RPMVSF_NODSA         if set, don't check header+payload DSA signature\n\
1375     rpm.RPMVSF_NORSA         if set, don't check header+payload RSA signature\n\
1376     rpm._RPMVSF_NODIGESTS    if set, don't check digest(s)\n\
1377     rpm._RPMVSF_NOSIGNATURES if set, don't check signature(s)\n" },
1378  {"getVSFlags",(PyCFunction) rpmts_GetVSFlags,  METH_NOARGS,
1379 "ts.getVSFlags() -> vsflags\n\
1380 - Retrieve current signature verification flags from transaction\n" },
1381  {"setColor",(PyCFunction) rpmts_SetColor,      METH_VARARGS|METH_KEYWORDS,
1382         NULL },
1383  {"pgpPrtPkts", (PyCFunction) rpmts_PgpPrtPkts, METH_VARARGS|METH_KEYWORDS,
1384         NULL },
1385  {"pgpImportPubkey",    (PyCFunction) rpmts_PgpImportPubkey,    METH_VARARGS|METH_KEYWORDS,
1386         NULL },
1387  {"getKeys",    (PyCFunction) rpmts_GetKeys,    METH_NOARGS,
1388         NULL },
1389  {"parseSpec",  (PyCFunction) spec_Parse,       METH_VARARGS|METH_KEYWORDS,
1390 "ts.parseSpec(\"/path/to/foo.spec\") -> spec\n\
1391 - Parse a spec file.\n" },
1392  {"dbMatch",    (PyCFunction) rpmts_Match,      METH_VARARGS|METH_KEYWORDS,
1393 "ts.dbMatch([TagN, [key, [len]]]) -> mi\n\
1394 - Create a match iterator for the default transaction rpmdb.\n" },
1395  {"next",               (PyCFunction)rpmts_Next,        METH_NOARGS,
1396 "ts.next() -> te\n\
1397 - Retrieve next transaction set element.\n" },
1398     {NULL,              NULL}           /* sentinel */
1399 };
1400
1401 /** \ingroup py_c
1402  */
1403 static void rpmts_dealloc(rpmtsObject * s)
1404 {
1405
1406 if (_rpmts_debug)
1407 fprintf(stderr, "%p -- ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1408     s->ts = rpmtsFree(s->ts);
1409
1410     if (s->scriptFd) Fclose(s->scriptFd);
1411     /* this will free the keyList, and decrement the ref count of all
1412        the items on the list as well :-) */
1413     Py_DECREF(s->keyList);
1414     PyObject_Del((PyObject *)s);
1415 }
1416
1417 static PyObject * rpmts_getattro(PyObject * o, PyObject * n)
1418 {
1419     return PyObject_GenericGetAttr(o, n);
1420 }
1421
1422 /** \ingroup py_c
1423  */
1424 static int rpmts_setattro(PyObject * o, PyObject * n, PyObject * v)
1425 {
1426     rpmtsObject *s = (rpmtsObject *)o;
1427     char * name = PyString_AsString(n);
1428     int fdno;
1429
1430     if (!strcmp(name, "scriptFd")) {
1431         if (!PyArg_Parse(v, "i", &fdno)) return 0;
1432         if (fdno < 0) {
1433             PyErr_SetString(PyExc_TypeError, "bad file descriptor");
1434             return -1;
1435         } else {
1436             s->scriptFd = fdDup(fdno);
1437             rpmtsSetScriptFd(s->ts, s->scriptFd);
1438         }
1439     } else {
1440         PyErr_SetString(PyExc_AttributeError, name);
1441         return -1;
1442     }
1443
1444     return 0;
1445 }
1446
1447 /** \ingroup py_c
1448  */
1449 static int rpmts_init(rpmtsObject * s, PyObject *args, PyObject *kwds)
1450 {
1451     /* nothing to do atm... */
1452     return 0;
1453 }
1454
1455 /** \ingroup py_c
1456  */
1457 static void rpmts_free(rpmtsObject * s)
1458 {
1459 if (_rpmts_debug)
1460 fprintf(stderr, "%p -- ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1461     s->ts = rpmtsFree(s->ts);
1462
1463     if (s->scriptFd)
1464         Fclose(s->scriptFd);
1465
1466     /* this will free the keyList, and decrement the ref count of all
1467        the items on the list as well :-) */
1468     Py_DECREF(s->keyList);
1469
1470     PyObject_Del((PyObject *)s);
1471 }
1472
1473 /** \ingroup py_c
1474  */
1475 static PyObject * rpmts_alloc(PyTypeObject * subtype, int nitems)
1476 {
1477     PyObject * s = PyType_GenericAlloc(subtype, nitems);
1478
1479 if (_rpmts_debug < 0)
1480 fprintf(stderr, "*** rpmts_alloc(%p,%d) ret %p\n", subtype, nitems, s);
1481     return s;
1482 }
1483
1484 /** \ingroup py_c
1485  */
1486 static PyObject * rpmts_new(PyTypeObject * subtype, PyObject *args, PyObject *kwds)
1487 {
1488     rpmtsObject * s = (void *) PyObject_New(rpmtsObject, subtype);
1489
1490     char * rootDir = "/";
1491     int vsflags = rpmExpandNumeric("%{?__vsflags}");
1492     char * kwlist[] = {"rootdir", "vsflags", 0};
1493
1494     if (_rpmts_debug < 0)
1495         fprintf(stderr, "*** rpmts_new(%p,%p,%p)\n", s, args, kwds);
1496
1497     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si:rpmts_init", kwlist,
1498             &rootDir, &vsflags))
1499         return NULL;
1500
1501     s->ts = rpmtsCreate();
1502     /* XXX: Why is there no rpmts_SetRootDir() ? */
1503     (void) rpmtsSetRootDir(s->ts, rootDir);
1504     /* XXX: make this use common code with rpmts_SetVSFlags() to check the
1505      *      python objects */
1506     (void) rpmtsSetVSFlags(s->ts, vsflags);
1507     s->keyList = PyList_New(0);
1508     s->scriptFd = NULL;
1509     s->tsi = NULL;
1510     s->tsiFilter = 0;
1511
1512     if (_rpmts_debug)
1513         fprintf(stderr, "%p ++ ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1514
1515     return (PyObject *)s;
1516 }
1517
1518 /**
1519  */
1520 static char rpmts_doc[] =
1521 "";
1522
1523 /** \ingroup py_c
1524  */
1525 PyTypeObject rpmts_Type = {
1526         PyObject_HEAD_INIT(&PyType_Type)
1527         0,                              /* ob_size */
1528         "rpm.ts",                       /* tp_name */
1529         sizeof(rpmtsObject),            /* tp_size */
1530         0,                              /* tp_itemsize */
1531         (destructor) rpmts_dealloc,     /* tp_dealloc */
1532         0,                              /* tp_print */
1533         (getattrfunc)0,                 /* tp_getattr */
1534         (setattrfunc)0,                 /* tp_setattr */
1535         0,                              /* tp_compare */
1536         0,                              /* tp_repr */
1537         0,                              /* tp_as_number */
1538         0,                              /* tp_as_sequence */
1539         0,                              /* tp_as_mapping */
1540         0,                              /* tp_hash */
1541         0,                              /* tp_call */
1542         0,                              /* tp_str */
1543         (getattrofunc) rpmts_getattro,  /* tp_getattro */
1544         (setattrofunc) rpmts_setattro,  /* tp_setattro */
1545         0,                              /* tp_as_buffer */
1546         Py_TPFLAGS_DEFAULT,             /* tp_flags */
1547         rpmts_doc,                      /* tp_doc */
1548 #if Py_TPFLAGS_HAVE_ITER
1549         0,                              /* tp_traverse */
1550         0,                              /* tp_clear */
1551         0,                              /* tp_richcompare */
1552         0,                              /* tp_weaklistoffset */
1553         (getiterfunc) rpmts_iter,       /* tp_iter */
1554         (iternextfunc) rpmts_iternext,  /* tp_iternext */
1555         rpmts_methods,                  /* tp_methods */
1556         0,                              /* tp_members */
1557         0,                              /* tp_getset */
1558         0,                              /* tp_base */
1559         0,                              /* tp_dict */
1560         0,                              /* tp_descr_get */
1561         0,                              /* tp_descr_set */
1562         0,                              /* tp_dictoffset */
1563         (initproc) rpmts_init,          /* tp_init */
1564         (allocfunc) rpmts_alloc,        /* tp_alloc */
1565         (newfunc) rpmts_new,            /* tp_new */
1566         (freefunc) rpmts_free,          /* tp_free */
1567         0,                              /* tp_is_gc */
1568 #endif
1569 };
1570
1571 /**
1572  */
1573 PyObject *
1574 rpmts_Create(PyObject * self, PyObject * args, PyObject * kwds)
1575 {
1576     return PyObject_Call((PyObject *) &rpmts_Type, args, kwds);
1577 }