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