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