We dont support ancient python versions...
[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 /** \ingroup py_c
144  */
145 struct rpmtsCallbackType_s {
146     PyObject * cb;
147     PyObject * data;
148     rpmtsObject * tso;
149     PyThreadState *_save;
150 };
151
152 /** \ingroup py_c
153  */
154 static PyObject *
155 rpmts_Debug(rpmtsObject * s, PyObject * args, PyObject * kwds)
156 {
157     char * kwlist[] = {"debugLevel", NULL};
158
159     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Debug", kwlist,
160             &_rpmts_debug))
161         return NULL;
162
163 if (_rpmts_debug < 0)
164 fprintf(stderr, "*** rpmts_Debug(%p) ts %p\n", s, s->ts);
165
166     Py_INCREF(Py_None);
167     return Py_None;
168 }
169
170 RPM_GNUC_NORETURN
171 static void die(PyObject *cb)
172 {
173     char *pyfn = NULL;
174     PyObject *r;
175
176     if (PyErr_Occurred()) {
177         PyErr_Print();
178     }
179     if ((r = PyObject_Repr(cb)) != NULL) { 
180         pyfn = PyString_AsString(r);
181     }
182     fprintf(stderr, _("error: python callback %s failed, aborting!\n"), 
183                       pyfn ? pyfn : "???");
184     rpmdbCheckTerminate(1);
185     exit(EXIT_FAILURE);
186 }
187
188 /** \ingroup py_c
189  */
190 static PyObject *
191 rpmts_AddInstall(rpmtsObject * s, PyObject * args, PyObject * kwds)
192 {
193     hdrObject * h;
194     PyObject * key;
195     char * how = "u";   /* XXX default to upgrade element if missing */
196     int isUpgrade = 0;
197     char * kwlist[] = {"header", "key", "how", NULL};
198     int rc = 0;
199
200     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|s:AddInstall", kwlist,
201             &hdr_Type, &h, &key, &how))
202         return NULL;
203
204     {   PyObject * hObj = (PyObject *) h;
205         if (hObj->ob_type != &hdr_Type) {
206             PyErr_SetString(PyExc_TypeError, "bad type for header argument");
207             return NULL;
208         }
209     }
210
211 if (_rpmts_debug < 0 || (_rpmts_debug > 0))
212 fprintf(stderr, "*** rpmts_AddInstall(%p,%p,%p,%s) ts %p\n", s, h, key, how, s->ts);
213
214     if (how && !rstreq(how, "u") && !rstreq(how, "i")) {
215         PyErr_SetString(PyExc_TypeError, "how argument must be \"u\" or \"i\"");
216         return NULL;
217     } else if (how && rstreq(how, "u"))
218         isUpgrade = 1;
219
220     rc = rpmtsAddInstallElement(s->ts, hdrGetHeader(h), key, isUpgrade, NULL);
221     if (rc) {
222         PyErr_SetString(pyrpmError, "adding package to transaction failed");
223         return NULL;
224     }
225         
226
227     /* This should increment the usage count for me */
228     if (key)
229         PyList_Append(s->keyList, key);
230
231     Py_INCREF(Py_None);
232     return Py_None;
233 }
234
235 /** \ingroup py_c
236  * @todo Permit finer control (i.e. not just --allmatches) of deleted elments.
237  */
238 static PyObject *
239 rpmts_AddErase(rpmtsObject * s, PyObject * args, PyObject * kwds)
240 {
241     PyObject * o;
242     int installed = 0;
243     rpmdbMatchIterator mi = NULL;
244     Header h;
245     char * kwlist[] = {"name", NULL};
246
247 if (_rpmts_debug)
248 fprintf(stderr, "*** rpmts_AddErase(%p) ts %p\n", s, s->ts);
249
250     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:AddErase", kwlist, &o))
251         return NULL;
252
253     if (PyString_Check(o)) {
254         char * name = PyString_AsString(o);
255         mi = rpmtsInitIterator(s->ts, RPMDBI_LABEL, name, 0);
256     } else if (PyInt_Check(o)) {
257         uint32_t recno = PyInt_AsLong(o);
258         mi = rpmtsInitIterator(s->ts, RPMDBI_PACKAGES, &recno, sizeof(recno));
259     } else {
260         PyErr_SetString(PyExc_TypeError, "string or integer expected");
261         return NULL;
262     }
263
264     while ((h = rpmdbNextIterator(mi)) != NULL) {
265         installed++;
266         rpmtsAddEraseElement(s->ts, h, -1);
267     }
268     rpmdbFreeIterator(mi);
269     
270     if (installed) {
271         Py_RETURN_NONE;
272     } else {
273         PyErr_SetString(pyrpmError, "package not installed");
274         return NULL;
275     }
276 }
277
278 /** \ingroup py_c
279  */
280 static int
281 rpmts_SolveCallback(rpmts ts, rpmds ds, const void * data)
282 {
283     struct rpmtsCallbackType_s * cbInfo = (struct rpmtsCallbackType_s *) data;
284     PyObject * args, * result;
285     int res = 1;
286
287 if (_rpmts_debug)
288 fprintf(stderr, "*** rpmts_SolveCallback(%p,%p,%p) \"%s\"\n", ts, ds, data, rpmdsDNEVR(ds));
289
290     if (cbInfo->tso == NULL) return res;
291     if (cbInfo->cb == Py_None) return res;
292
293     PyEval_RestoreThread(cbInfo->_save);
294
295     args = Py_BuildValue("(Oissi)", cbInfo->tso,
296                 rpmdsTagN(ds), rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds));
297     result = PyEval_CallObject(cbInfo->cb, args);
298     Py_DECREF(args);
299
300     if (!result) {
301         die(cbInfo->cb);
302     } else {
303         if (PyInt_Check(result))
304             res = PyInt_AsLong(result);
305         Py_DECREF(result);
306     }
307
308     cbInfo->_save = PyEval_SaveThread();
309
310     return res;
311 }
312
313 /** \ingroup py_c
314  */
315 static PyObject *
316 rpmts_Check(rpmtsObject * s, PyObject * args, PyObject * kwds)
317 {
318     rpmps ps;
319     rpmProblem p;
320     PyObject * list, * cf;
321     struct rpmtsCallbackType_s cbInfo;
322     int xx;
323     char * kwlist[] = {"callback", NULL};
324
325     memset(&cbInfo, 0, sizeof(cbInfo));
326     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:Check", kwlist,
327             &cbInfo.cb))
328         return NULL;
329
330     if (cbInfo.cb != NULL) {
331         if (!PyCallable_Check(cbInfo.cb)) {
332             PyErr_SetString(PyExc_TypeError, "expected a callable");
333             return NULL;
334         }
335         xx = rpmtsSetSolveCallback(s->ts, rpmts_SolveCallback, (void *)&cbInfo);
336     }
337
338 if (_rpmts_debug)
339 fprintf(stderr, "*** rpmts_Check(%p) ts %p cb %p\n", s, s->ts, cbInfo.cb);
340
341     cbInfo.tso = s;
342     cbInfo._save = PyEval_SaveThread();
343
344     xx = rpmtsCheck(s->ts);
345     ps = rpmtsProblems(s->ts);
346
347     PyEval_RestoreThread(cbInfo._save);
348
349     if (ps != NULL) {
350         list = PyList_New(0);
351         rpmpsi psi = rpmpsInitIterator(ps);
352
353         /* XXX TODO: rpmlib >= 4.0.3 can return multiple suggested keys. */
354         while (rpmpsNextIterator(psi) >= 0) {
355             char * altNEVR, * needsName;
356             char * byName, * byVersion, * byRelease, *byArch;
357             char * needsOP, * needsVersion;
358             rpmsenseFlags needsFlags, sense;
359             fnpyKey key;
360
361             p = rpmpsGetProblem(psi);
362
363             byName = xstrdup(rpmProblemGetPkgNEVR(p));
364             if ((byArch= strrchr(byName, '.')) != NULL)
365                 *byArch++ = '\0';
366             if ((byRelease = strrchr(byName, '-')) != NULL)
367                 *byRelease++ = '\0';
368             if ((byVersion = strrchr(byName, '-')) != NULL)
369                 *byVersion++ = '\0';
370
371             key = rpmProblemGetKey(p);
372
373             altNEVR = needsName = xstrdup(rpmProblemGetAltNEVR(p));
374             if (needsName[1] == ' ') {
375                 sense = (needsName[0] == 'C')
376                         ? RPMDEP_SENSE_CONFLICTS : RPMDEP_SENSE_REQUIRES;
377                 needsName += 2;
378             } else
379                 sense = RPMDEP_SENSE_REQUIRES;
380             if ((needsVersion = strrchr(needsName, ' ')) != NULL)
381                 *needsVersion++ = '\0';
382
383             needsFlags = 0;
384             if ((needsOP = strrchr(needsName, ' ')) != NULL) {
385                 for (*needsOP++ = '\0'; *needsOP != '\0'; needsOP++) {
386                     if (*needsOP == '<')        needsFlags |= RPMSENSE_LESS;
387                     else if (*needsOP == '>')   needsFlags |= RPMSENSE_GREATER;
388                     else if (*needsOP == '=')   needsFlags |= RPMSENSE_EQUAL;
389                 }
390             }
391
392             cf = Py_BuildValue("((sss)(ss)iOi)", byName, byVersion, byRelease,
393                                needsName, needsVersion, needsFlags,
394                                (key != NULL ? key : Py_None),
395                                sense);
396             PyList_Append(list, (PyObject *) cf);
397             Py_DECREF(cf);
398             free(byName);
399             free(altNEVR);
400         }
401
402         psi = rpmpsFreeIterator(psi);
403         ps = rpmpsFree(ps);
404
405         return list;
406     }
407
408     Py_INCREF(Py_None);
409     return Py_None;
410 }
411
412 /** \ingroup py_c
413  */
414 static PyObject *
415 rpmts_Order(rpmtsObject * s)
416 {
417     int rc;
418
419 if (_rpmts_debug)
420 fprintf(stderr, "*** rpmts_Order(%p) ts %p\n", s, s->ts);
421
422     Py_BEGIN_ALLOW_THREADS
423     rc = rpmtsOrder(s->ts);
424     Py_END_ALLOW_THREADS
425
426     return Py_BuildValue("i", rc);
427 }
428
429 /** \ingroup py_c
430  */
431 static PyObject *
432 rpmts_Clean(rpmtsObject * s)
433 {
434 if (_rpmts_debug)
435 fprintf(stderr, "*** rpmts_Clean(%p) ts %p\n", s, s->ts);
436
437     rpmtsClean(s->ts);
438
439     Py_INCREF(Py_None);
440     return Py_None;
441 }
442
443 /** \ingroup py_c
444  */
445 static PyObject *
446 rpmts_OpenDB(rpmtsObject * s)
447 {
448     int dbmode;
449
450 if (_rpmts_debug)
451 fprintf(stderr, "*** rpmts_OpenDB(%p) ts %p\n", s, s->ts);
452
453     dbmode = rpmtsGetDBMode(s->ts);
454     if (dbmode == -1)
455         dbmode = O_RDONLY;
456
457     return Py_BuildValue("i", rpmtsOpenDB(s->ts, dbmode));
458 }
459
460 /** \ingroup py_c
461  */
462 static PyObject *
463 rpmts_CloseDB(rpmtsObject * s)
464 {
465     int rc;
466
467 if (_rpmts_debug)
468 fprintf(stderr, "*** rpmts_CloseDB(%p) ts %p\n", s, s->ts);
469
470     rc = rpmtsCloseDB(s->ts);
471     rpmtsSetDBMode(s->ts, -1);  /* XXX disable lazy opens */
472
473     return Py_BuildValue("i", rc);
474 }
475
476 /** \ingroup py_c
477  */
478 static PyObject *
479 rpmts_InitDB(rpmtsObject * s)
480 {
481     int rc;
482
483 if (_rpmts_debug)
484 fprintf(stderr, "*** rpmts_InitDB(%p) ts %p\n", s, s->ts);
485
486     rc = rpmtsInitDB(s->ts, O_RDONLY);
487     if (rc == 0)
488         rc = rpmtsCloseDB(s->ts);
489
490     return Py_BuildValue("i", rc);
491 }
492
493 /** \ingroup py_c
494  */
495 static PyObject *
496 rpmts_RebuildDB(rpmtsObject * s)
497 {
498     int rc;
499
500 if (_rpmts_debug)
501 fprintf(stderr, "*** rpmts_RebuildDB(%p) ts %p\n", s, s->ts);
502
503     Py_BEGIN_ALLOW_THREADS
504     rc = rpmtsRebuildDB(s->ts);
505     Py_END_ALLOW_THREADS
506
507     return Py_BuildValue("i", rc);
508 }
509
510 /** \ingroup py_c
511  */
512 static PyObject *
513 rpmts_VerifyDB(rpmtsObject * s)
514 {
515     int rc;
516
517 if (_rpmts_debug)
518 fprintf(stderr, "*** rpmts_VerifyDB(%p) ts %p\n", s, s->ts);
519
520     Py_BEGIN_ALLOW_THREADS
521     rc = rpmtsVerifyDB(s->ts);
522     Py_END_ALLOW_THREADS
523
524     return Py_BuildValue("i", rc);
525 }
526
527 /** \ingroup py_c
528  */
529 static PyObject *
530 rpmts_HdrFromFdno(rpmtsObject * s, PyObject * args, PyObject * kwds)
531 {
532     PyObject * result = NULL;
533     Header h;
534     FD_t fd;
535     int fdno;
536     rpmRC rpmrc;
537     char * kwlist[] = {"fd", NULL};
538
539     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:HdrFromFdno", kwlist,
540             &fdno))
541         return NULL;
542
543     fd = fdDup(fdno);
544     rpmrc = rpmReadPackageFile(s->ts, fd, "rpmts_HdrFromFdno", &h);
545     Fclose(fd);
546
547 if (_rpmts_debug)
548 fprintf(stderr, "*** rpmts_HdrFromFdno(%p) ts %p rc %d\n", s, s->ts, rpmrc);
549
550     switch (rpmrc) {
551     case RPMRC_OK:
552         if (h)
553             result = Py_BuildValue("N", hdr_Wrap(h));
554         h = headerFree(h);      /* XXX ref held by result */
555         break;
556
557     case RPMRC_NOKEY:
558         PyErr_SetString(pyrpmError, "public key not available");
559         break;
560
561     case RPMRC_NOTTRUSTED:
562         PyErr_SetString(pyrpmError, "public key not trusted");
563         break;
564
565     case RPMRC_NOTFOUND:
566     case RPMRC_FAIL:
567     default:
568         PyErr_SetString(pyrpmError, "error reading package header");
569         break;
570     }
571
572     return result;
573 }
574
575 /** \ingroup py_c
576  */
577 static PyObject *
578 rpmts_HdrCheck(rpmtsObject * s, PyObject * args, PyObject * kwds)
579 {
580     PyObject * blob;
581     PyObject * result = NULL;
582     char * msg = NULL;
583     const void * uh;
584     int uc;
585     rpmRC rpmrc;
586     char * kwlist[] = {"headers", NULL};
587
588 if (_rpmts_debug)
589 fprintf(stderr, "*** rpmts_HdrCheck(%p) ts %p\n", s, s->ts);
590
591     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:HdrCheck", kwlist, &blob))
592         return NULL;
593
594     if (blob == Py_None) {
595         Py_INCREF(Py_None);
596         return Py_None;
597     }
598     if (!PyString_Check(blob)) {
599         PyErr_SetString(pyrpmError, "hdrCheck takes a string of octets");
600         return result;
601     }
602     uh = PyString_AsString(blob);
603     uc = PyString_Size(blob);
604
605     rpmrc = headerCheck(s->ts, uh, uc, &msg);
606
607     switch (rpmrc) {
608     case RPMRC_OK:
609         Py_INCREF(Py_None);
610         result = Py_None;
611         break;
612
613     case RPMRC_NOKEY:
614         PyErr_SetString(pyrpmError, "public key not availaiable");
615         break;
616
617     case RPMRC_NOTTRUSTED:
618         PyErr_SetString(pyrpmError, "public key not trusted");
619         break;
620
621     case RPMRC_FAIL:
622     default:
623         PyErr_SetString(pyrpmError, msg);
624         break;
625     }
626     msg = _free(msg);
627
628     return result;
629 }
630
631 /** \ingroup py_c
632  */
633 static PyObject *
634 rpmts_SetVSFlags(rpmtsObject * s, PyObject * args, PyObject * kwds)
635 {
636     rpmVSFlags vsflags;
637     char * kwlist[] = {"flags", NULL};
638
639 if (_rpmts_debug)
640 fprintf(stderr, "*** rpmts_SetVSFlags(%p) ts %p\n", s, s->ts);
641
642     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetVSFlags", kwlist,
643             &vsflags))
644         return NULL;
645
646     /* XXX FIXME: value check on vsflags, or build pure python object 
647      * for it, and require an object of that type */
648
649     return Py_BuildValue("i", rpmtsSetVSFlags(s->ts, vsflags));
650 }
651
652 /** \ingroup py_c
653  */
654 static PyObject *
655 rpmts_GetVSFlags(rpmtsObject * s)
656 {
657     return Py_BuildValue("i", rpmtsVSFlags(s->ts));
658 }
659
660 /** \ingroup py_c
661  */
662 static PyObject *
663 rpmts_SetColor(rpmtsObject * s, PyObject * args, PyObject * kwds)
664 {
665     rpm_color_t tscolor;
666     char * kwlist[] = {"color", NULL};
667
668 if (_rpmts_debug)
669 fprintf(stderr, "*** rpmts_SetColor(%p) ts %p\n", s, s->ts);
670
671     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:Color", kwlist, &tscolor))
672         return NULL;
673
674     /* XXX FIXME: value check on tscolor, or build pure python object
675      * for it, and require an object of that type */
676
677     return Py_BuildValue("i", rpmtsSetColor(s->ts, tscolor));
678 }
679
680 /** \ingroup py_c
681  */
682 static PyObject *
683 rpmts_PgpPrtPkts(rpmtsObject * s, PyObject * args, PyObject * kwds)
684 {
685     PyObject * blob;
686     unsigned char * pkt;
687     unsigned int pktlen;
688     int rc;
689     char * kwlist[] = {"octets", NULL};
690
691 if (_rpmts_debug)
692 fprintf(stderr, "*** rpmts_PgpPrtPkts(%p) ts %p\n", s, s->ts);
693
694     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PgpPrtPkts", kwlist, &blob))
695         return NULL;
696
697     if (blob == Py_None) {
698         Py_INCREF(Py_None);
699         return Py_None;
700     }
701     if (!PyString_Check(blob)) {
702         PyErr_SetString(pyrpmError, "pgpPrtPkts takes a string of octets");
703         return NULL;
704     }
705     pkt = (unsigned char *)PyString_AsString(blob);
706     pktlen = PyString_Size(blob);
707
708     rc = pgpPrtPkts(pkt, pktlen, NULL, 1);
709
710     return Py_BuildValue("i", rc);
711 }
712
713 /** \ingroup py_c
714  */
715 static PyObject *
716 rpmts_PgpImportPubkey(rpmtsObject * s, PyObject * args, PyObject * kwds)
717 {
718     PyObject * blob;
719     unsigned char * pkt;
720     unsigned int pktlen;
721     int rc;
722     char * kwlist[] = {"pubkey", NULL};
723
724 if (_rpmts_debug)
725 fprintf(stderr, "*** rpmts_PgpImportPubkey(%p) ts %p\n", s, s->ts);
726
727     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:PgpImportPubkey",
728             kwlist, &blob))
729         return NULL;
730
731     if (blob == Py_None) {
732         Py_INCREF(Py_None);
733         return Py_None;
734     }
735     if (!PyString_Check(blob)) {
736         PyErr_SetString(pyrpmError, "PgpImportPubkey takes a string of octets");
737         return NULL;
738     }
739     pkt = (unsigned char *)PyString_AsString(blob);
740     pktlen = PyString_Size(blob);
741
742     rc = rpmtsImportPubkey(s->ts, pkt, pktlen);
743
744     return Py_BuildValue("i", rc);
745 }
746
747 /** \ingroup py_c
748  */
749 static PyObject *
750 rpmts_GetKeys(rpmtsObject * s)
751 {
752     const void **data = NULL;
753     int num, i;
754     PyObject *tuple;
755
756 if (_rpmts_debug)
757 fprintf(stderr, "*** rpmts_GetKeys(%p) ts %p\n", s, s->ts);
758
759     rpmtsGetKeys(s->ts, &data, &num);
760     if (data == NULL || num <= 0) {
761         data = _free(data);
762         Py_INCREF(Py_None);
763         return Py_None;
764     }
765
766     tuple = PyTuple_New(num);
767
768     for (i = 0; i < num; i++) {
769         PyObject *obj;
770         obj = (data[i] ? (PyObject *) data[i] : Py_None);
771         Py_INCREF(obj);
772         PyTuple_SetItem(tuple, i, obj);
773     }
774
775     data = _free(data);
776
777     return tuple;
778 }
779
780 /** \ingroup py_c
781  */
782 static void *
783 rpmtsCallback(const void * hd, const rpmCallbackType what,
784                          const rpm_loff_t amount, const rpm_loff_t total,
785                          const void * pkgKey, rpmCallbackData data)
786 {
787     Header h = (Header) hd;
788     struct rpmtsCallbackType_s * cbInfo = data;
789     PyObject * pkgObj = (PyObject *) pkgKey;
790     PyObject * args, * result;
791     static FD_t fd;
792
793     if (cbInfo->cb == Py_None) return NULL;
794
795     /* Synthesize a python object for callback (if necessary). */
796     if (pkgObj == NULL) {
797         if (h) {
798             pkgObj = Py_BuildValue("s", headerGetString(h, RPMTAG_NAME));
799         } else {
800             pkgObj = Py_None;
801             Py_INCREF(pkgObj);
802         }
803     } else
804         Py_INCREF(pkgObj);
805
806     PyEval_RestoreThread(cbInfo->_save);
807
808     args = Py_BuildValue("(iLLOO)", what, amount, total, pkgObj, cbInfo->data);
809     result = PyEval_CallObject(cbInfo->cb, args);
810     Py_DECREF(args);
811     Py_DECREF(pkgObj);
812
813     if (!result) {
814         die(cbInfo->cb);
815     }
816
817     if (what == RPMCALLBACK_INST_OPEN_FILE) {
818         int fdno;
819
820         if (!PyArg_Parse(result, "i", &fdno)) {
821             die(cbInfo->cb);
822         }
823         Py_DECREF(result);
824         cbInfo->_save = PyEval_SaveThread();
825
826         fd = fdDup(fdno);
827 if (_rpmts_debug)
828 fprintf(stderr, "\t%p = fdDup(%d)\n", fd, fdno);
829
830         fcntl(Fileno(fd), F_SETFD, FD_CLOEXEC);
831
832         return fd;
833     } else
834     if (what == RPMCALLBACK_INST_CLOSE_FILE) {
835 if (_rpmts_debug)
836 fprintf(stderr, "\tFclose(%p)\n", fd);
837         Fclose (fd);
838     } else {
839 if (_rpmts_debug)
840 fprintf(stderr, "\t%llu:%llu key %p\n", (long long) amount, (long long) total, pkgKey);
841     }
842
843     Py_DECREF(result);
844     cbInfo->_save = PyEval_SaveThread();
845
846     return NULL;
847 }
848
849 /** \ingroup py_c
850  */
851 static PyObject *
852 rpmts_SetFlags(rpmtsObject * s, PyObject * args, PyObject * kwds)
853 {
854     rpmtransFlags transFlags = 0;
855     char * kwlist[] = {"flags", NULL};
856
857     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:SetFlags", kwlist,
858             &transFlags))
859         return NULL;
860
861 if (_rpmts_debug)
862 fprintf(stderr, "*** rpmts_SetFlags(%p) ts %p transFlags %x\n", s, s->ts, transFlags);
863
864     /* XXX FIXME: value check on flags, or build pure python object 
865      * for it, and require an object of that type */
866
867     return Py_BuildValue("i", rpmtsSetFlags(s->ts, transFlags));
868 }
869
870 /** \ingroup py_c
871  */
872 static PyObject *
873 rpmts_SetProbFilter(rpmtsObject * s, PyObject * args, PyObject * kwds)
874 {
875     rpmprobFilterFlags ignoreSet = 0;
876     rpmprobFilterFlags oignoreSet;
877     char * kwlist[] = {"ignoreSet", NULL};
878
879     if (!PyArg_ParseTupleAndKeywords(args, kwds, "i:ProbFilter", kwlist,
880             &ignoreSet))
881         return NULL;
882
883 if (_rpmts_debug)
884 fprintf(stderr, "*** rpmts_SetProbFilter(%p) ts %p ignoreSet %x\n", s, s->ts, ignoreSet);
885
886     oignoreSet = s->ignoreSet;
887     s->ignoreSet = ignoreSet;
888
889     return Py_BuildValue("i", oignoreSet);
890 }
891
892 /** \ingroup py_c
893  */
894 static rpmpsObject *
895 rpmts_Problems(rpmtsObject * s)
896 {
897
898 if (_rpmts_debug)
899 fprintf(stderr, "*** rpmts_Problems(%p) ts %p\n", s, s->ts);
900
901     return rpmps_Wrap( rpmtsProblems(s->ts) );
902 }
903
904 /** \ingroup py_c
905  */
906 static PyObject *
907 rpmts_Run(rpmtsObject * s, PyObject * args, PyObject * kwds)
908 {
909     int rc;
910     PyObject * list;
911     rpmps ps;
912     rpmpsi psi;
913     struct rpmtsCallbackType_s cbInfo;
914     char * kwlist[] = {"callback", "data", NULL};
915
916     if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:Run", kwlist,
917             &cbInfo.cb, &cbInfo.data))
918         return NULL;
919
920     cbInfo.tso = s;
921     cbInfo._save = PyEval_SaveThread();
922
923     if (cbInfo.cb != NULL) {
924         if (!PyCallable_Check(cbInfo.cb)) {
925             PyErr_SetString(PyExc_TypeError, "expected a callable");
926             return NULL;
927         }
928         (void) rpmtsSetNotifyCallback(s->ts, rpmtsCallback, (void *) &cbInfo);
929     }
930
931 if (_rpmts_debug)
932 fprintf(stderr, "*** rpmts_Run(%p) ts %p ignore %x\n", s, s->ts, s->ignoreSet);
933
934     rc = rpmtsRun(s->ts, NULL, s->ignoreSet);
935     ps = rpmtsProblems(s->ts);
936
937     if (cbInfo.cb)
938         (void) rpmtsSetNotifyCallback(s->ts, NULL, NULL);
939
940     PyEval_RestoreThread(cbInfo._save);
941
942     if (rc < 0) {
943         list = PyList_New(0);
944         return list;
945     } else if (!rc) {
946         Py_INCREF(Py_None);
947         return Py_None;
948     }
949
950     list = PyList_New(0);
951     psi = rpmpsInitIterator(ps);
952     while (rpmpsNextIterator(psi) >= 0) {
953         rpmProblem p = rpmpsGetProblem(psi);
954         char * ps = rpmProblemString(p);
955         PyObject * prob = Py_BuildValue("s(isN)", ps,
956                              rpmProblemGetType(p),
957                              rpmProblemGetStr(p),
958                              PyLong_FromLongLong(rpmProblemGetDiskNeed(p)));
959         PyList_Append(list, prob);
960         free(ps);
961         Py_DECREF(prob);
962     }
963
964     psi = rpmpsFreeIterator(psi);
965     ps = rpmpsFree(ps);
966
967     return list;
968 }
969
970 static PyObject *
971 rpmts_iter(rpmtsObject * s)
972 {
973 if (_rpmts_debug)
974 fprintf(stderr, "*** rpmts_iter(%p) ts %p\n", s, s->ts);
975
976     Py_INCREF(s);
977     return (PyObject *)s;
978 }
979
980 /**
981  * @todo Add TR_ADDED filter to iterator.
982  */
983 static PyObject *
984 rpmts_iternext(rpmtsObject * s)
985 {
986     PyObject * result = NULL;
987     rpmte te;
988
989 if (_rpmts_debug)
990 fprintf(stderr, "*** rpmts_iternext(%p) ts %p tsi %p %d\n", s, s->ts, s->tsi, s->tsiFilter);
991
992     /* Reset iterator on 1st entry. */
993     if (s->tsi == NULL) {
994         s->tsi = rpmtsiInit(s->ts);
995         if (s->tsi == NULL)
996             return NULL;
997         s->tsiFilter = 0;
998     }
999
1000     te = rpmtsiNext(s->tsi, s->tsiFilter);
1001     if (te != NULL) {
1002         result = (PyObject *) rpmte_Wrap(te);
1003     } else {
1004         s->tsi = rpmtsiFree(s->tsi);
1005         s->tsiFilter = 0;
1006     }
1007
1008     return result;
1009 }
1010
1011 /**
1012  * @todo Add TR_ADDED filter to iterator.
1013  */
1014 static PyObject *
1015 rpmts_Next(rpmtsObject * s)
1016 {
1017     PyObject * result;
1018
1019 if (_rpmts_debug)
1020 fprintf(stderr, "*** rpmts_Next(%p) ts %p\n", s, s->ts);
1021
1022     result = rpmts_iternext(s);
1023
1024     if (result == NULL) {
1025         Py_INCREF(Py_None);
1026         return Py_None;
1027     }
1028
1029     return result;
1030 }
1031
1032 /**
1033  */
1034 static specObject *
1035 spec_Parse(rpmtsObject * s, PyObject * args, PyObject * kwds)
1036 {
1037     const char * specfile;
1038     rpmSpec spec;
1039     char * buildRoot = NULL;
1040     int recursing = 0;
1041     char * passPhrase = "";
1042     char *cookie = NULL;
1043     int anyarch = 1;
1044     int force = 1;
1045     char * kwlist[] = {"specfile", NULL};
1046
1047     if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:Parse", kwlist, &specfile))
1048         return NULL;
1049
1050     if (parseSpec(s->ts, specfile,"/", buildRoot,recursing, passPhrase,
1051              cookie, anyarch, force)!=0) {
1052              PyErr_SetString(pyrpmError, "can't parse specfile\n");
1053                      return NULL;
1054    }
1055
1056     spec = rpmtsSpec(s->ts);
1057     return spec_Wrap(spec);
1058 }
1059
1060 /**
1061  */
1062 static rpmmiObject *
1063 rpmts_Match(rpmtsObject * s, PyObject * args, PyObject * kwds)
1064 {
1065     PyObject *TagN = NULL;
1066     PyObject *Key = NULL;
1067     char *key = NULL;
1068 /* XXX lkey *must* be a 32 bit integer, int "works" on all known platforms. */
1069     int lkey = 0;
1070     int len = 0;
1071     rpmTag tag = RPMDBI_PACKAGES;
1072     char * kwlist[] = {"tagNumber", "key", NULL};
1073
1074 if (_rpmts_debug)
1075 fprintf(stderr, "*** rpmts_Match(%p) ts %p\n", s, s->ts);
1076
1077     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:Match", kwlist,
1078             &TagN, &Key))
1079         return NULL;
1080
1081     if (TagN && (tag = tagNumFromPyObject (TagN)) == -1) {
1082         PyErr_SetString(PyExc_TypeError, "unknown tag type");
1083         return NULL;
1084     }
1085
1086     if (Key) {
1087         if (PyString_Check(Key)) {
1088             key = PyString_AsString(Key);
1089             len = PyString_Size(Key);
1090         } else if (PyInt_Check(Key)) {
1091             lkey = PyInt_AsLong(Key);
1092             key = (char *)&lkey;
1093             len = sizeof(lkey);
1094         } else {
1095             PyErr_SetString(PyExc_TypeError, "unknown key type");
1096             return NULL;
1097         }
1098         /* One of the conversions above failed, exception is set already */
1099         if (PyErr_Occurred()) {
1100             return NULL;
1101         }
1102     }
1103
1104     /* XXX If not already opened, open the database O_RDONLY now. */
1105     /* XXX FIXME: lazy default rdonly open also done by rpmtsInitIterator(). */
1106     if (rpmtsGetRdb(s->ts) == NULL) {
1107         int rc = rpmtsOpenDB(s->ts, O_RDONLY);
1108         if (rc || rpmtsGetRdb(s->ts) == NULL) {
1109             PyErr_SetString(PyExc_TypeError, "rpmdb open failed");
1110             return NULL;
1111         }
1112     }
1113
1114     return rpmmi_Wrap( rpmtsInitIterator(s->ts, tag, key, len), (PyObject*)s);
1115 }
1116
1117 /** \ingroup py_c
1118  */
1119 static struct PyMethodDef rpmts_methods[] = {
1120  {"Debug",      (PyCFunction)rpmts_Debug,       METH_VARARGS|METH_KEYWORDS,
1121         NULL},
1122
1123  {"addInstall", (PyCFunction) rpmts_AddInstall, METH_VARARGS|METH_KEYWORDS,
1124         NULL },
1125  {"addErase",   (PyCFunction) rpmts_AddErase,   METH_VARARGS|METH_KEYWORDS,
1126         NULL },
1127  {"check",      (PyCFunction) rpmts_Check,      METH_VARARGS|METH_KEYWORDS,
1128         NULL },
1129  {"order",      (PyCFunction) rpmts_Order,      METH_NOARGS,
1130         NULL },
1131  {"setFlags",   (PyCFunction) rpmts_SetFlags,   METH_VARARGS|METH_KEYWORDS,
1132 "ts.setFlags(transFlags) -> previous transFlags\n\
1133 - Set control bit(s) for executing ts.run().\n\
1134   Note: This method replaces the 1st argument to the old ts.run()\n" },
1135  {"setProbFilter",      (PyCFunction) rpmts_SetProbFilter,      METH_VARARGS|METH_KEYWORDS,
1136 "ts.setProbFilter(ignoreSet) -> previous ignoreSet\n\
1137 - Set control bit(s) for ignoring problems found by ts.run().\n\
1138   Note: This method replaces the 2nd argument to the old ts.run()\n" },
1139  {"problems",   (PyCFunction) rpmts_Problems,   METH_NOARGS,
1140 "ts.problems() -> ps\n\
1141 - Return current problem set.\n" },
1142  {"run",        (PyCFunction) rpmts_Run,        METH_VARARGS|METH_KEYWORDS,
1143 "ts.run(callback, data) -> (problems)\n\
1144 - Run a transaction set, returning list of problems found.\n\
1145   Note: The callback may not be None.\n" },
1146  {"clean",      (PyCFunction) rpmts_Clean,      METH_NOARGS,
1147         NULL },
1148  {"openDB",     (PyCFunction) rpmts_OpenDB,     METH_NOARGS,
1149 "ts.openDB() -> None\n\
1150 - Open the default transaction rpmdb.\n\
1151   Note: The transaction rpmdb is lazily opened, so ts.openDB() is seldom needed.\n" },
1152  {"closeDB",    (PyCFunction) rpmts_CloseDB,    METH_NOARGS,
1153 "ts.closeDB() -> None\n\
1154 - Close the default transaction rpmdb.\n\
1155   Note: ts.closeDB() disables lazy opens, and should hardly ever be used.\n" },
1156  {"initDB",     (PyCFunction) rpmts_InitDB,     METH_NOARGS,
1157 "ts.initDB() -> None\n\
1158 - Initialize the default transaction rpmdb.\n\
1159  Note: ts.initDB() is seldom needed anymore.\n" },
1160  {"rebuildDB",  (PyCFunction) rpmts_RebuildDB,  METH_NOARGS,
1161 "ts.rebuildDB() -> None\n\
1162 - Rebuild the default transaction rpmdb.\n" },
1163  {"verifyDB",   (PyCFunction) rpmts_VerifyDB,   METH_NOARGS,
1164 "ts.verifyDB() -> None\n\
1165 - Verify the default transaction rpmdb.\n" },
1166  {"hdrFromFdno",(PyCFunction) rpmts_HdrFromFdno,METH_VARARGS|METH_KEYWORDS,
1167 "ts.hdrFromFdno(fdno) -> hdr\n\
1168 - Read a package header from a file descriptor.\n" },
1169  {"hdrCheck",   (PyCFunction) rpmts_HdrCheck,   METH_VARARGS|METH_KEYWORDS,
1170         NULL },
1171  {"setVSFlags",(PyCFunction) rpmts_SetVSFlags,  METH_VARARGS|METH_KEYWORDS,
1172 "ts.setVSFlags(vsflags) -> ovsflags\n\
1173 - Set signature verification flags. Values for vsflags are:\n\
1174     rpm.RPMVSF_NOHDRCHK      if set, don't check rpmdb headers\n\
1175     rpm.RPMVSF_NEEDPAYLOAD   if not set, check header+payload (if possible)\n\
1176     rpm.RPMVSF_NOSHA1HEADER  if set, don't check header SHA1 digest\n\
1177     rpm.RPMVSF_NODSAHEADER   if set, don't check header DSA signature\n\
1178     rpm.RPMVSF_NOMD5         if set, don't check header+payload MD5 digest\n\
1179     rpm.RPMVSF_NODSA         if set, don't check header+payload DSA signature\n\
1180     rpm.RPMVSF_NORSA         if set, don't check header+payload RSA signature\n\
1181     rpm._RPMVSF_NODIGESTS    if set, don't check digest(s)\n\
1182     rpm._RPMVSF_NOSIGNATURES if set, don't check signature(s)\n" },
1183  {"getVSFlags",(PyCFunction) rpmts_GetVSFlags,  METH_NOARGS,
1184 "ts.getVSFlags() -> vsflags\n\
1185 - Retrieve current signature verification flags from transaction\n" },
1186  {"setColor",(PyCFunction) rpmts_SetColor,      METH_VARARGS|METH_KEYWORDS,
1187         NULL },
1188  {"pgpPrtPkts", (PyCFunction) rpmts_PgpPrtPkts, METH_VARARGS|METH_KEYWORDS,
1189         NULL },
1190  {"pgpImportPubkey",    (PyCFunction) rpmts_PgpImportPubkey,    METH_VARARGS|METH_KEYWORDS,
1191         NULL },
1192  {"getKeys",    (PyCFunction) rpmts_GetKeys,    METH_NOARGS,
1193         NULL },
1194  {"parseSpec",  (PyCFunction) spec_Parse,       METH_VARARGS|METH_KEYWORDS,
1195 "ts.parseSpec(\"/path/to/foo.spec\") -> spec\n\
1196 - Parse a spec file.\n" },
1197  {"dbMatch",    (PyCFunction) rpmts_Match,      METH_VARARGS|METH_KEYWORDS,
1198 "ts.dbMatch([TagN, [key, [len]]]) -> mi\n\
1199 - Create a match iterator for the default transaction rpmdb.\n" },
1200  {"next",               (PyCFunction)rpmts_Next,        METH_NOARGS,
1201 "ts.next() -> te\n\
1202 - Retrieve next transaction set element.\n" },
1203     {NULL,              NULL}           /* sentinel */
1204 };
1205
1206 /** \ingroup py_c
1207  */
1208 static void rpmts_dealloc(rpmtsObject * s)
1209 {
1210
1211 if (_rpmts_debug)
1212 fprintf(stderr, "%p -- ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1213     s->ts = rpmtsFree(s->ts);
1214
1215     if (s->scriptFd) Fclose(s->scriptFd);
1216     /* this will free the keyList, and decrement the ref count of all
1217        the items on the list as well :-) */
1218     Py_DECREF(s->keyList);
1219     PyObject_Del((PyObject *)s);
1220 }
1221
1222 static PyObject * rpmts_getattro(PyObject * o, PyObject * n)
1223 {
1224     return PyObject_GenericGetAttr(o, n);
1225 }
1226
1227 /** \ingroup py_c
1228  */
1229 static int rpmts_setattro(PyObject * o, PyObject * n, PyObject * v)
1230 {
1231     rpmtsObject *s = (rpmtsObject *)o;
1232     char * name = PyString_AsString(n);
1233     int fdno;
1234
1235     if (rstreq(name, "scriptFd")) {
1236         if (!PyArg_Parse(v, "i", &fdno)) return 0;
1237         if (fdno < 0) {
1238             PyErr_SetString(PyExc_TypeError, "bad file descriptor");
1239             return -1;
1240         } else {
1241             s->scriptFd = fdDup(fdno);
1242             rpmtsSetScriptFd(s->ts, s->scriptFd);
1243         }
1244     } else {
1245         PyErr_SetString(PyExc_AttributeError, name);
1246         return -1;
1247     }
1248
1249     return 0;
1250 }
1251
1252 /** \ingroup py_c
1253  */
1254 static int rpmts_init(rpmtsObject * s, PyObject *args, PyObject *kwds)
1255 {
1256     /* nothing to do atm... */
1257     return 0;
1258 }
1259
1260 /** \ingroup py_c
1261  */
1262 static void rpmts_free(rpmtsObject * s)
1263 {
1264 if (_rpmts_debug)
1265 fprintf(stderr, "%p -- ts %p db %p\n", s, s->ts, rpmtsGetRdb(s->ts));
1266     s->ts = rpmtsFree(s->ts);
1267
1268     if (s->scriptFd)
1269         Fclose(s->scriptFd);
1270
1271     /* this will free the keyList, and decrement the ref count of all
1272        the items on the list as well :-) */
1273     Py_DECREF(s->keyList);
1274
1275     PyObject_Del((PyObject *)s);
1276 }
1277
1278 /** \ingroup py_c
1279  */
1280 static PyObject * rpmts_alloc(PyTypeObject * subtype, int nitems)
1281 {
1282     PyObject * s = PyType_GenericAlloc(subtype, nitems);
1283
1284 if (_rpmts_debug < 0)
1285 fprintf(stderr, "*** rpmts_alloc(%p,%d) ret %p\n", subtype, nitems, s);
1286     return 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         (allocfunc) rpmts_alloc,        /* 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 }