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