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