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