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