5d6bf8f4a07cdbb927834c7a6f055a6dd0e8acb4
[platform/upstream/rpm.git] / python / rpmts-py.c
1 #include "rpmsystem-py.h"
2
3 #include <rpm/rpmlib.h> /* rpmReadPackageFile, headerCheck */
4 #include <rpm/rpmtag.h>
5 #include <rpm/rpmpgp.h>
6 #include <rpm/rpmdb.h>
7 #include <rpm/rpmbuild.h>
8
9 #include "header-py.h"
10 #include "rpmds-py.h"   /* XXX for rpmdsNew */
11 #include "rpmfd-py.h"
12 #include "rpmfi-py.h"   /* XXX for rpmfiNew */
13 #include "rpmmi-py.h"
14 #include "rpmps-py.h"
15 #include "rpmte-py.h"
16
17 #include "rpmts-py.h"
18
19 #include "debug.h"
20
21 /** \ingroup python
22  * \name Class: Rpmts
23  * \class Rpmts
24  * \brief A python rpm.ts object represents an RPM transaction set.
25  *
26  * The transaction set is the workhorse of RPM.  It performs the
27  * installation and upgrade of packages.  The rpm.ts object is
28  * instantiated by the TransactionSet function in the rpm module.
29  *
30  * The TransactionSet function takes two optional arguments. The first
31  * argument is the root path. The second is the verify signature disable flags,
32  * a set of the following bits:
33  *
34  * -    rpm.RPMVSF_NOHDRCHK     if set, don't check rpmdb headers
35  * -    rpm.RPMVSF_NEEDPAYLOAD  if not set, check header+payload (if possible)
36  * -    rpm.RPMVSF_NOSHA1HEADER if set, don't check header SHA1 digest
37  * -    rpm.RPMVSF_NODSAHEADER  if set, don't check header DSA signature
38  * -    rpm.RPMVSF_NOMD5        if set, don't check header+payload MD5 digest
39  * -    rpm.RPMVSF_NODSA        if set, don't check header+payload DSA signature
40  * -    rpm.RPMVSF_NORSA        if set, don't check header+payload RSA signature
41  *
42  * For convenience, there are the following masks:
43  * -    rpm._RPMVSF_NODIGESTS           if set, don't check digest(s).
44  * -    rpm._RPMVSF_NOSIGNATURES        if set, don't check signature(s).
45  *
46  * A rpm.ts object has the following methods:
47  *
48  * - addInstall(hdr,data,mode)  Add an install element to a transaction set.
49  * @param hdr   the header to be added
50  * @param data  user data that will be passed to the transaction callback
51  *              during transaction execution
52  * @param mode  optional argument that specifies if this package should
53  *              be installed ('i'), upgraded ('u').
54  *
55  * - addErase(name) Add an erase element to a transaction set.
56  * @param name  the package name to be erased
57  *
58  * - check()    Perform a dependency check on the transaction set. After
59  *              headers have been added to a transaction set, a dependency
60  *              check can be performed to make sure that all package
61  *              dependencies are satisfied.
62  * @return      None If there are no unresolved dependencies
63  *              Otherwise a list of complex tuples is returned, one tuple per
64  *              unresolved dependency, with
65  * The format of the dependency tuple is:
66  *     ((packageName, packageVersion, packageRelease),
67  *      (reqName, reqVersion),
68  *      needsFlags,
69  *      suggestedPackage,
70  *      sense)
71  *     packageName, packageVersion, packageRelease are the name,
72  *     version, and release of the package that has the unresolved
73  *     dependency or conflict.
74  *     The reqName and reqVersion are the name and version of the
75  *     requirement or conflict.
76  *     The needsFlags is a bitfield that describes the versioned
77  *     nature of a requirement or conflict.  The constants
78  *     rpm.RPMSENSE_LESS, rpm.RPMSENSE_GREATER, and
79  *     rpm.RPMSENSE_EQUAL can be logical ANDed with the needsFlags
80  *     to get versioned dependency information.
81  *     suggestedPackage is a tuple if the dependency check was aware
82  *     of a package that solves this dependency problem when the
83  *     dependency check was run.  Packages that are added to the
84  *     transaction set as "available" are examined during the
85  *     dependency check as possible dependency solvers. The tuple
86  *     contains two values, (header, suggestedName).  These are set to
87  *     the header of the suggested package and its name, respectively.
88  *     If there is no known package to solve the dependency problem,
89  *     suggestedPackage is None.
90  *     The constants rpm.RPMDEP_SENSE_CONFLICTS and
91  *     rpm.RPMDEP_SENSE_REQUIRES are set to show a dependency as a
92  *     requirement or a conflict.
93  *
94  * - ts.order() Do a topological sort of added element relations.
95  * @return      None
96  *
97  * - ts.setFlags(transFlags) Set transaction set flags.
98  * @param transFlags - bit(s) to controll transaction operations. The
99  *              following values can be logically OR'ed together:
100  *      - rpm.RPMTRANS_FLAG_TEST - test mode, do not modify the RPM
101  *              database, change any files, or run any package scripts
102  *      - rpm.RPMTRANS_FLAG_BUILD_PROBS - only build a list of
103  *              problems encountered when attempting to run this transaction
104  *              set
105  *      - rpm.RPMTRANS_FLAG_NOSCRIPTS - do not execute package scripts
106  *      - rpm.RPMTRANS_FLAG_JUSTDB - only make changes to the rpm
107  *              database, do not modify files.
108  *      - rpm.RPMTRANS_FLAG_NOTRIGGERS - do not run trigger scripts
109  *      - rpm.RPMTRANS_FLAG_NODOCS - do not install files marked as %doc
110  *      - rpm.RPMTRANS_FLAG_ALLFILES - create all files, even if a
111  *              file is marked %config(missingok) and an upgrade is
112  *              being performed.
113  *      - rpm.RPMTRANS_FLAG_KEEPOBSOLETE - do not remove obsoleted
114  *              packages.
115  * @return      previous transFlags
116  *
117  * - ts.setProbFilter(ignoreSet) Set transaction set problem filter.
118  * @param problemSetFilter - control bit(s) to ignore classes of problems,
119  *              a logical or of one or more of the following bit(s):
120  *      - rpm.RPMPROB_FILTER_IGNOREOS -
121  *      - rpm.RPMPROB_FILTER_IGNOREARCH -
122  *      - rpm.RPMPROB_FILTER_REPLACEPKG -
123  *      - rpm.RPMPROB_FILTER_FORCERELOCATE -
124  *      - rpm.RPMPROB_FILTER_REPLACENEWFILES -
125  *      - rpm.RPMPROB_FILTER_REPLACEOLDFILES -
126  *      - rpm.RPMPROB_FILTER_OLDPACKAGE -
127  *      - rpm.RPMPROB_FILTER_DISKSPACE -
128  * @return      previous ignoreSet
129  *
130  * - ts.run(callback,data) Attempt to execute a transaction set.
131  *      After the transaction set has been populated with install/upgrade or
132  *      erase actions, the transaction set can be executed by invoking
133  *      the ts.run() method.
134  */
135
136 struct rpmtsObject_s {
137     PyObject_HEAD
138     PyObject *md_dict;          /*!< to look like PyModuleObject */
139     rpmts       ts;
140     FD_t scriptFd;
141     rpmtsi tsi;
142 };
143
144 struct rpmtsCallbackType_s {
145     PyObject * cb;
146     PyObject * data;
147     rpmtsObject * tso;
148     PyThreadState *_save;
149 };
150
151 RPM_GNUC_NORETURN
152 static void die(PyObject *cb)
153 {
154     char *pyfn = NULL;
155     PyObject *r;
156
157     if (PyErr_Occurred()) {
158         PyErr_Print();
159     }
160     if ((r = PyObject_Repr(cb)) != NULL) { 
161         pyfn = PyString_AsString(r);
162     }
163     fprintf(stderr, _("error: python callback %s failed, aborting!\n"), 
164                       pyfn ? pyfn : "???");
165     rpmdbCheckTerminate(1);
166     exit(EXIT_FAILURE);
167 }
168
169 static PyObject *
170 rpmts_AddInstall(rpmtsObject * s, PyObject * args)
171 {
172     Header h = NULL;
173     PyObject * key;
174     int how = 0;
175     int rc;
176
177     if (!PyArg_ParseTuple(args, "O&Oi:AddInstall", 
178                           hdrFromPyObject, &h, &key, &how))
179         return NULL;
180
181     rc = rpmtsAddInstallElement(s->ts, h, key, how, NULL);
182     return PyBool_FromLong((rc == 0));
183 }
184
185 static PyObject *
186 rpmts_AddErase(rpmtsObject * s, PyObject * args)
187 {
188     Header h;
189
190     if (!PyArg_ParseTuple(args, "O&:AddErase", hdrFromPyObject, &h))
191         return NULL;
192
193     return PyBool_FromLong(rpmtsAddEraseElement(s->ts, h, -1) == 0);
194 }
195
196 static int
197 rpmts_SolveCallback(rpmts ts, rpmds ds, const void * data)
198 {
199     struct rpmtsCallbackType_s * cbInfo = (struct rpmtsCallbackType_s *) data;
200     PyObject * args, * result;
201     int res = 1;
202
203     if (cbInfo->tso == NULL) return res;
204     if (cbInfo->cb == Py_None) return res;
205
206     PyEval_RestoreThread(cbInfo->_save);
207
208     args = Py_BuildValue("(Oissi)", cbInfo->tso,
209                 rpmdsTagN(ds), rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
210     result = PyEval_CallObject(cbInfo->cb, args);
211     Py_DECREF(args);
212
213     if (!result) {
214         die(cbInfo->cb);
215     } else {
216         if (PyInt_Check(result))
217             res = PyInt_AsLong(result);
218         Py_DECREF(result);
219     }
220
221     cbInfo->_save = PyEval_SaveThread();
222
223     return res;
224 }
225
226 static PyObject *
227 rpmts_Check(rpmtsObject * s, PyObject * args, PyObject * kwds)
228 {
229     struct rpmtsCallbackType_s cbInfo;
230     int rc;
231     char * kwlist[] = {"callback", NULL};
232
233     memset(&cbInfo, 0, sizeof(cbInfo));
234     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:Check", kwlist,
235             &cbInfo.cb))
236         return NULL;
237
238     if (cbInfo.cb != NULL) {
239         if (!PyCallable_Check(cbInfo.cb)) {
240             PyErr_SetString(PyExc_TypeError, "expected a callable");
241             return NULL;
242         }
243         rc = rpmtsSetSolveCallback(s->ts, rpmts_SolveCallback, (void *)&cbInfo);
244     }
245
246     cbInfo.tso = s;
247     cbInfo._save = PyEval_SaveThread();
248
249     rc = rpmtsCheck(s->ts);
250
251     PyEval_RestoreThread(cbInfo._save);
252
253     return PyBool_FromLong((rc == 0));
254 }
255
256 static PyObject *
257 rpmts_Order(rpmtsObject * s)
258 {
259     int rc;
260
261     Py_BEGIN_ALLOW_THREADS
262     rc = rpmtsOrder(s->ts);
263     Py_END_ALLOW_THREADS
264
265     return Py_BuildValue("i", rc);
266 }
267
268 static PyObject *
269 rpmts_Clean(rpmtsObject * s)
270 {
271     rpmtsClean(s->ts);
272
273     Py_RETURN_NONE;
274 }
275
276 static PyObject *
277 rpmts_OpenDB(rpmtsObject * s)
278 {
279     int dbmode;
280
281     dbmode = rpmtsGetDBMode(s->ts);
282     if (dbmode == -1)
283         dbmode = O_RDONLY;
284
285     return Py_BuildValue("i", rpmtsOpenDB(s->ts, dbmode));
286 }
287
288 static PyObject *
289 rpmts_CloseDB(rpmtsObject * s)
290 {
291     int rc;
292
293     rc = rpmtsCloseDB(s->ts);
294     rpmtsSetDBMode(s->ts, -1);  /* XXX disable lazy opens */
295
296     return Py_BuildValue("i", rc);
297 }
298
299 static PyObject *
300 rpmts_InitDB(rpmtsObject * s)
301 {
302     int rc;
303
304     rc = rpmtsInitDB(s->ts, O_RDONLY);
305     if (rc == 0)
306         rc = rpmtsCloseDB(s->ts);
307
308     return Py_BuildValue("i", rc);
309 }
310
311 static PyObject *
312 rpmts_RebuildDB(rpmtsObject * s)
313 {
314     int rc;
315
316     Py_BEGIN_ALLOW_THREADS
317     rc = rpmtsRebuildDB(s->ts);
318     Py_END_ALLOW_THREADS
319
320     return Py_BuildValue("i", rc);
321 }
322
323 static PyObject *
324 rpmts_VerifyDB(rpmtsObject * s)
325 {
326     int rc;
327
328     Py_BEGIN_ALLOW_THREADS
329     rc = rpmtsVerifyDB(s->ts);
330     Py_END_ALLOW_THREADS
331
332     return Py_BuildValue("i", rc);
333 }
334
335 static PyObject *
336 rpmts_HdrFromFdno(rpmtsObject * s, PyObject * args, PyObject * kwds)
337 {
338     PyObject * result = NULL;
339     Header h;
340     FD_t fd;
341     rpmRC rpmrc;
342     char * kwlist[] = {"fd", NULL};
343
344     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&:HdrFromFdno", kwlist,
345                                      rpmFdFromPyObject, &fd))
346         return NULL;
347
348     rpmrc = rpmReadPackageFile(s->ts, fd, "rpmts_HdrFromFdno", &h);
349     Fclose(fd);
350
351     switch (rpmrc) {
352     case RPMRC_OK:
353         if (h)
354             result = Py_BuildValue("N", hdr_Wrap(&hdr_Type, h));
355         h = headerFree(h);      /* XXX ref held by result */
356         break;
357
358     case RPMRC_NOKEY:
359         PyErr_SetString(pyrpmError, "public key not available");
360         break;
361
362     case RPMRC_NOTTRUSTED:
363         PyErr_SetString(pyrpmError, "public key not trusted");
364         break;
365
366     case RPMRC_NOTFOUND:
367     case RPMRC_FAIL:
368     default:
369         PyErr_SetString(pyrpmError, "error reading package header");
370         break;
371     }
372
373     return result;
374 }
375
376 static PyObject *
377 rpmts_HdrCheck(rpmtsObject * s, PyObject * args, PyObject * kwds)
378 {
379     PyObject * blob;
380     PyObject * result = NULL;
381     char * msg = NULL;
382     const void * uh;
383     int uc;
384     rpmRC rpmrc;
385     char * kwlist[] = {"headers", NULL};
386
387     if (!PyArg_ParseTupleAndKeywords(args, kwds, "S:HdrCheck", kwlist, &blob))
388         return NULL;
389
390     uh = PyString_AsString(blob);
391     uc = PyString_Size(blob);
392
393     rpmrc = headerCheck(s->ts, uh, uc, &msg);
394
395     switch (rpmrc) {
396     case RPMRC_OK:
397         Py_INCREF(Py_None);
398         result = Py_None;
399         break;
400
401     case RPMRC_NOKEY:
402         PyErr_SetString(pyrpmError, "public key not availaiable");
403         break;
404
405     case RPMRC_NOTTRUSTED:
406         PyErr_SetString(pyrpmError, "public key not trusted");
407         break;
408
409     case RPMRC_FAIL:
410     default:
411         PyErr_SetString(pyrpmError, msg);
412         break;
413     }
414     msg = _free(msg);
415
416     return result;
417 }
418
419 static PyObject *
420 rpmts_PgpPrtPkts(rpmtsObject * s, PyObject * args, PyObject * kwds)
421 {
422     PyObject * blob;
423     unsigned char * pkt;
424     unsigned int pktlen;
425     int rc;
426     char * kwlist[] = {"octets", NULL};
427
428     if (!PyArg_ParseTupleAndKeywords(args, kwds, "S:PgpPrtPkts", kwlist, &blob))
429         return NULL;
430
431     pkt = (unsigned char *)PyString_AsString(blob);
432     pktlen = PyString_Size(blob);
433
434     rc = pgpPrtPkts(pkt, pktlen, NULL, 1);
435
436     return Py_BuildValue("i", rc);
437 }
438
439 static PyObject *
440 rpmts_PgpImportPubkey(rpmtsObject * s, PyObject * args, PyObject * kwds)
441 {
442     PyObject * blob;
443     unsigned char * pkt;
444     unsigned int pktlen;
445     int rc;
446     char * kwlist[] = {"pubkey", NULL};
447
448     if (!PyArg_ParseTupleAndKeywords(args, kwds, "S:PgpImportPubkey",
449             kwlist, &blob))
450         return NULL;
451
452     pkt = (unsigned char *)PyString_AsString(blob);
453     pktlen = PyString_Size(blob);
454
455     rc = rpmtsImportPubkey(s->ts, pkt, pktlen);
456
457     return Py_BuildValue("i", rc);
458 }
459
460 static void *
461 rpmtsCallback(const void * hd, const rpmCallbackType what,
462                          const rpm_loff_t amount, const rpm_loff_t total,
463                          const void * pkgKey, rpmCallbackData data)
464 {
465     Header h = (Header) hd;
466     struct rpmtsCallbackType_s * cbInfo = data;
467     PyObject * pkgObj = (PyObject *) pkgKey;
468     PyObject * args, * result;
469     static FD_t fd;
470
471     if (cbInfo->cb == Py_None) return NULL;
472
473     /* Synthesize a python object for callback (if necessary). */
474     if (pkgObj == NULL) {
475         if (h) {
476             pkgObj = Py_BuildValue("s", headerGetString(h, RPMTAG_NAME));
477         } else {
478             pkgObj = Py_None;
479             Py_INCREF(pkgObj);
480         }
481     } else
482         Py_INCREF(pkgObj);
483
484     PyEval_RestoreThread(cbInfo->_save);
485
486     args = Py_BuildValue("(iLLOO)", what, amount, total, pkgObj, cbInfo->data);
487     result = PyEval_CallObject(cbInfo->cb, args);
488     Py_DECREF(args);
489     Py_DECREF(pkgObj);
490
491     if (!result) {
492         die(cbInfo->cb);
493     }
494
495     if (what == RPMCALLBACK_INST_OPEN_FILE) {
496         int fdno;
497
498         if (!PyArg_Parse(result, "i", &fdno)) {
499             die(cbInfo->cb);
500         }
501         Py_DECREF(result);
502         cbInfo->_save = PyEval_SaveThread();
503
504         fd = fdDup(fdno);
505         fcntl(Fileno(fd), F_SETFD, FD_CLOEXEC);
506
507         return fd;
508     } else
509     if (what == RPMCALLBACK_INST_CLOSE_FILE) {
510         Fclose (fd);
511     }
512
513     Py_DECREF(result);
514     cbInfo->_save = PyEval_SaveThread();
515
516     return NULL;
517 }
518
519 static PyObject *
520 rpmts_Problems(rpmtsObject * s)
521 {
522     return rpmps_Wrap(&rpmps_Type, rpmtsProblems(s->ts));
523 }
524
525 static PyObject *
526 rpmts_Run(rpmtsObject * s, PyObject * args, PyObject * kwds)
527 {
528     int rc;
529     struct rpmtsCallbackType_s cbInfo;
530     rpmprobFilterFlags ignoreSet;
531     char * kwlist[] = {"callback", "data", "ignoreSet", NULL};
532
533     if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi:Run", kwlist,
534             &cbInfo.cb, &cbInfo.data, &ignoreSet))
535         return NULL;
536
537     cbInfo.tso = s;
538     cbInfo._save = PyEval_SaveThread();
539
540     if (cbInfo.cb != NULL) {
541         if (!PyCallable_Check(cbInfo.cb)) {
542             PyErr_SetString(PyExc_TypeError, "expected a callable");
543             return NULL;
544         }
545         (void) rpmtsSetNotifyCallback(s->ts, rpmtsCallback, (void *) &cbInfo);
546     }
547
548     rc = rpmtsRun(s->ts, NULL, ignoreSet);
549
550     if (cbInfo.cb)
551         (void) rpmtsSetNotifyCallback(s->ts, NULL, NULL);
552
553     PyEval_RestoreThread(cbInfo._save);
554
555     return Py_BuildValue("i", rc);
556 }
557
558 static PyObject *
559 rpmts_iternext(rpmtsObject * s)
560 {
561     PyObject * result = NULL;
562     rpmte te;
563
564     /* Reset iterator on 1st entry. */
565     if (s->tsi == NULL) {
566         s->tsi = rpmtsiInit(s->ts);
567         if (s->tsi == NULL)
568             return NULL;
569     }
570
571     te = rpmtsiNext(s->tsi, 0);
572     if (te != NULL) {
573         result = rpmte_Wrap(&rpmte_Type, te);
574     } else {
575         s->tsi = rpmtsiFree(s->tsi);
576     }
577
578     return result;
579 }
580
581 static PyObject *
582 rpmts_Match(rpmtsObject * s, PyObject * args, PyObject * kwds)
583 {
584     PyObject *Key = NULL;
585     char *key = NULL;
586 /* XXX lkey *must* be a 32 bit integer, int "works" on all known platforms. */
587     int lkey = 0;
588     int len = 0;
589     rpmTag tag = RPMDBI_PACKAGES;
590     char * kwlist[] = {"tagNumber", "key", NULL};
591
592     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&O:Match", kwlist,
593             tagNumFromPyObject, &tag, &Key))
594         return NULL;
595
596     if (Key) {
597         if (PyString_Check(Key)) {
598             key = PyString_AsString(Key);
599             len = PyString_Size(Key);
600         } else if (PyInt_Check(Key)) {
601             lkey = PyInt_AsLong(Key);
602             key = (char *)&lkey;
603             len = sizeof(lkey);
604         } else {
605             PyErr_SetString(PyExc_TypeError, "unknown key type");
606             return NULL;
607         }
608         /* One of the conversions above failed, exception is set already */
609         if (PyErr_Occurred()) {
610             return NULL;
611         }
612     }
613
614     /* XXX If not already opened, open the database O_RDONLY now. */
615     /* XXX FIXME: lazy default rdonly open also done by rpmtsInitIterator(). */
616     if (rpmtsGetRdb(s->ts) == NULL) {
617         int rc = rpmtsOpenDB(s->ts, O_RDONLY);
618         if (rc || rpmtsGetRdb(s->ts) == NULL) {
619             PyErr_SetString(PyExc_TypeError, "rpmdb open failed");
620             return NULL;
621         }
622     }
623
624     return rpmmi_Wrap(&rpmmi_Type, rpmtsInitIterator(s->ts, tag, key, len), (PyObject*)s);
625 }
626
627 static struct PyMethodDef rpmts_methods[] = {
628  {"addInstall", (PyCFunction) rpmts_AddInstall, METH_VARARGS,
629         NULL },
630  {"addErase",   (PyCFunction) rpmts_AddErase,   METH_VARARGS|METH_KEYWORDS,
631         NULL },
632  {"check",      (PyCFunction) rpmts_Check,      METH_VARARGS|METH_KEYWORDS,
633         NULL },
634  {"order",      (PyCFunction) rpmts_Order,      METH_NOARGS,
635         NULL },
636  {"problems",   (PyCFunction) rpmts_Problems,   METH_NOARGS,
637 "ts.problems() -> ps\n\
638 - Return current problem set.\n" },
639  {"run",        (PyCFunction) rpmts_Run,        METH_VARARGS|METH_KEYWORDS,
640 "ts.run(callback, data) -> (problems)\n\
641 - Run a transaction set, returning list of problems found.\n\
642   Note: The callback may not be None.\n" },
643  {"clean",      (PyCFunction) rpmts_Clean,      METH_NOARGS,
644         NULL },
645  {"openDB",     (PyCFunction) rpmts_OpenDB,     METH_NOARGS,
646 "ts.openDB() -> None\n\
647 - Open the default transaction rpmdb.\n\
648   Note: The transaction rpmdb is lazily opened, so ts.openDB() is seldom needed.\n" },
649  {"closeDB",    (PyCFunction) rpmts_CloseDB,    METH_NOARGS,
650 "ts.closeDB() -> None\n\
651 - Close the default transaction rpmdb.\n\
652   Note: ts.closeDB() disables lazy opens, and should hardly ever be used.\n" },
653  {"initDB",     (PyCFunction) rpmts_InitDB,     METH_NOARGS,
654 "ts.initDB() -> None\n\
655 - Initialize the default transaction rpmdb.\n\
656  Note: ts.initDB() is seldom needed anymore.\n" },
657  {"rebuildDB",  (PyCFunction) rpmts_RebuildDB,  METH_NOARGS,
658 "ts.rebuildDB() -> None\n\
659 - Rebuild the default transaction rpmdb.\n" },
660  {"verifyDB",   (PyCFunction) rpmts_VerifyDB,   METH_NOARGS,
661 "ts.verifyDB() -> None\n\
662 - Verify the default transaction rpmdb.\n" },
663  {"hdrFromFdno",(PyCFunction) rpmts_HdrFromFdno,METH_VARARGS|METH_KEYWORDS,
664 "ts.hdrFromFdno(fdno) -> hdr\n\
665 - Read a package header from a file descriptor.\n" },
666  {"hdrCheck",   (PyCFunction) rpmts_HdrCheck,   METH_VARARGS|METH_KEYWORDS,
667         NULL },
668  {"pgpPrtPkts", (PyCFunction) rpmts_PgpPrtPkts, METH_VARARGS|METH_KEYWORDS,
669         NULL },
670  {"pgpImportPubkey",    (PyCFunction) rpmts_PgpImportPubkey,    METH_VARARGS|METH_KEYWORDS,
671         NULL },
672  {"dbMatch",    (PyCFunction) rpmts_Match,      METH_VARARGS|METH_KEYWORDS,
673 "ts.dbMatch([TagN, [key, [len]]]) -> mi\n\
674 - Create a match iterator for the default transaction rpmdb.\n" },
675     {NULL,              NULL}           /* sentinel */
676 };
677
678 static void rpmts_dealloc(rpmtsObject * s)
679 {
680
681     s->ts = rpmtsFree(s->ts);
682
683     if (s->scriptFd) Fclose(s->scriptFd);
684     s->ob_type->tp_free((PyObject *)s);
685 }
686
687 static PyObject * rpmts_new(PyTypeObject * subtype, PyObject *args, PyObject *kwds)
688 {
689     char * rootDir = "/";
690     rpmVSFlags vsflags = rpmExpandNumeric("%{?__vsflags}");
691     char * kwlist[] = {"rootdir", "vsflags", 0};
692     rpmts ts = NULL;
693
694     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si:rpmts_new", kwlist,
695             &rootDir, &vsflags))
696         return NULL;
697
698     ts = rpmtsCreate();
699     (void) rpmtsSetRootDir(ts, rootDir);
700     /* XXX: make this use common code with rpmts_SetVSFlags() to check the
701      *      python objects */
702     (void) rpmtsSetVSFlags(ts, vsflags);
703
704     return rpmts_Wrap(subtype, ts);
705 }
706
707 static PyObject *rpmts_get_tid(rpmtsObject *s, void *closure)
708 {
709     return Py_BuildValue("i", rpmtsGetTid(s->ts));
710 }
711
712 static PyObject *rpmts_get_rootDir(rpmtsObject *s, void *closure)
713 {
714     return Py_BuildValue("s", rpmtsRootDir(s->ts));
715 }
716
717 static int rpmts_set_scriptFd(rpmtsObject *s, PyObject *value, void *closure)
718 {
719     int rc = 0;
720     if (PyArg_Parse(value, "O&", rpmFdFromPyObject, &s->scriptFd)) {
721         rpmtsSetScriptFd(s->ts, s->scriptFd);
722     } else if (value == Py_None) {
723         Fclose(s->scriptFd);
724         s->scriptFd = NULL;
725         rpmtsSetScriptFd(s->ts, NULL);
726     } else {
727         rc = -1;
728     }
729     return rc;
730 }
731
732 static PyObject *rpmts_get_color(rpmtsObject *s, void *closure)
733 {
734     return Py_BuildValue("i", rpmtsColor(s->ts));
735 }
736
737 static PyObject *rpmts_get_prefcolor(rpmtsObject *s, void *closure)
738 {
739     return Py_BuildValue("i", rpmtsPrefColor(s->ts));
740 }
741
742 static int rpmts_set_color(rpmtsObject *s, PyObject *value, void *closure)
743 {
744     rpm_color_t color;
745     if (!PyArg_Parse(value, "i", &color)) return -1;
746
747     /* TODO: validate the bits */
748     rpmtsSetColor(s->ts, color);
749     return 0;
750 }
751
752 static int rpmts_set_prefcolor(rpmtsObject *s, PyObject *value, void *closure)
753 {
754     rpm_color_t color;
755     if (!PyArg_Parse(value, "i", &color)) return -1;
756
757     /* TODO: validate the bits */
758     rpmtsSetPrefColor(s->ts, color);
759     return 0;
760 }
761
762 static int rpmts_set_flags(rpmtsObject *s, PyObject *value, void *closure)
763 {
764     rpmtransFlags flags;
765     if (!PyArg_Parse(value, "i", &flags)) return -1;
766
767     /* TODO: validate the bits */
768     rpmtsSetFlags(s->ts, flags);
769     return 0;
770 }
771
772 static int rpmts_set_vsflags(rpmtsObject *s, PyObject *value, void *closure)
773 {
774     rpmVSFlags flags;
775     if (!PyArg_Parse(value, "i", &flags)) return -1;
776
777     /* TODO: validate the bits */
778     rpmtsSetVSFlags(s->ts, flags);
779     return 0;
780 }
781
782 static PyObject *rpmts_get_flags(rpmtsObject *s, void *closure)
783 {
784     return Py_BuildValue("i", rpmtsFlags(s->ts));
785 }
786
787 static PyObject *rpmts_get_vsflags(rpmtsObject *s, void *closure)
788 {
789     return Py_BuildValue("i", rpmtsVSFlags(s->ts));
790 }
791
792 static char rpmts_doc[] =
793 "";
794
795 static PyGetSetDef rpmts_getseters[] = {
796         /* only provide a setter until we have rpmfd wrappings */
797         {"scriptFd",    NULL,   (setter)rpmts_set_scriptFd, NULL },
798         {"tid",         (getter)rpmts_get_tid, NULL, NULL },
799         {"rootDir",     (getter)rpmts_get_rootDir, NULL, NULL },
800         {"_color",      (getter)rpmts_get_color, (setter)rpmts_set_color, NULL},
801         {"_prefcolor",  (getter)rpmts_get_prefcolor, (setter)rpmts_set_prefcolor, NULL},
802         {"_flags",      (getter)rpmts_get_flags, (setter)rpmts_set_flags, NULL},
803         {"_vsflags",    (getter)rpmts_get_vsflags, (setter)rpmts_set_vsflags, NULL},
804         { NULL }
805 };
806
807 PyTypeObject rpmts_Type = {
808         PyObject_HEAD_INIT(&PyType_Type)
809         0,                              /* ob_size */
810         "rpm.ts",                       /* tp_name */
811         sizeof(rpmtsObject),            /* tp_size */
812         0,                              /* tp_itemsize */
813         (destructor) rpmts_dealloc,     /* tp_dealloc */
814         0,                              /* tp_print */
815         (getattrfunc)0,                 /* tp_getattr */
816         (setattrfunc)0,                 /* tp_setattr */
817         0,                              /* tp_compare */
818         0,                              /* tp_repr */
819         0,                              /* tp_as_number */
820         0,                              /* tp_as_sequence */
821         0,                              /* tp_as_mapping */
822         0,                              /* tp_hash */
823         0,                              /* tp_call */
824         0,                              /* tp_str */
825         PyObject_GenericGetAttr,        /* tp_getattro */
826         PyObject_GenericSetAttr,        /* tp_setattro */
827         0,                              /* tp_as_buffer */
828         Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
829         rpmts_doc,                      /* tp_doc */
830         0,                              /* tp_traverse */
831         0,                              /* tp_clear */
832         0,                              /* tp_richcompare */
833         0,                              /* tp_weaklistoffset */
834         PyObject_SelfIter,              /* tp_iter */
835         (iternextfunc) rpmts_iternext,  /* tp_iternext */
836         rpmts_methods,                  /* tp_methods */
837         0,                              /* tp_members */
838         rpmts_getseters,                /* tp_getset */
839         0,                              /* tp_base */
840         0,                              /* tp_dict */
841         0,                              /* tp_descr_get */
842         0,                              /* tp_descr_set */
843         0,                              /* tp_dictoffset */
844         0,                              /* tp_init */
845         0,                              /* tp_alloc */
846         (newfunc) rpmts_new,            /* tp_new */
847         0,                              /* tp_free */
848         0,                              /* tp_is_gc */
849 };
850
851 PyObject * rpmts_Wrap(PyTypeObject *subtype, rpmts ts)
852 {
853     rpmtsObject * s = (rpmtsObject *)subtype->tp_alloc(subtype, 0);
854     if (s == NULL) return PyErr_NoMemory();
855
856     s->ts = ts;
857     s->scriptFd = NULL;
858     s->tsi = NULL;
859     return (PyObject *) s;
860 }