1 /*
2  * Copyright (C) 2012-2014 Red Hat, Inc.
3  *
4  * Licensed under the GNU Lesser General Public License Version 2.1
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <Python.h>
22 #include <solv/poolid.h>
23 #include <solv/solver.h>
24 #include <solv/util.h>
25 #include <time.h>
26 
27 #include "error.hpp"
28 #include "nevra.hpp"
29 #include "hy-query-private.hpp"
30 #include "hy-selector.h"
31 #include "hy-subject.h"
32 #include "dnf-reldep.h"
33 #include "dnf-reldep-list.h"
34 #include "repo/solvable/DependencyContainer.hpp"
35 #include "transaction/Swdb.hpp"
36 
37 #include "exception-py.hpp"
38 #include "hawkey-pysys.hpp"
39 #include "iutil-py.hpp"
40 #include "package-py.hpp"
41 #include "query-py.hpp"
42 #include "reldep-py.hpp"
43 #include "sack-py.hpp"
44 #include "pycomp.hpp"
45 #include "sack/advisorypkg.hpp"
46 #include "sack/packageset.hpp"
47 #include "sack/selector.hpp"
48 
49 #include <algorithm>
50 #include <functional>
51 
52 typedef struct {
53     PyObject_HEAD
54     HyQuery query;
55     PyObject *sack;
56 } _QueryObject;
57 
58 static const int keyname_int_matches[] = {
59     HY_PKG,
60     HY_PKG_ADVISORY,
61     HY_PKG_ADVISORY_BUG,
62     HY_PKG_ADVISORY_CVE,
63     HY_PKG_ADVISORY_SEVERITY,
64     HY_PKG_ADVISORY_TYPE,
65     HY_PKG_ARCH,
66     HY_PKG_CONFLICTS,
67     HY_PKG_DESCRIPTION,
68     HY_PKG_DOWNGRADABLE,
69     HY_PKG_DOWNGRADES,
70     HY_PKG_EMPTY,
71     HY_PKG_ENHANCES,
72     HY_PKG_EPOCH,
73     HY_PKG_EVR,
74     HY_PKG_FILE,
75     HY_PKG_LATEST,
76     HY_PKG_LATEST_PER_ARCH,
77     HY_PKG_LATEST_PER_ARCH_BY_PRIORITY,
78     HY_PKG_LOCATION,
79     HY_PKG_NAME,
80     HY_PKG_NEVRA,
81     HY_PKG_NEVRA_STRICT,
82     HY_PKG_OBSOLETES,
83     HY_PKG_OBSOLETES_BY_PRIORITY,
84     HY_PKG_PROVIDES,
85     HY_PKG_RECOMMENDS,
86     HY_PKG_RELEASE,
87     HY_PKG_REPONAME,
88     HY_PKG_REQUIRES,
89     HY_PKG_SOURCERPM,
90     HY_PKG_SUGGESTS,
91     HY_PKG_SUMMARY,
92     HY_PKG_SUPPLEMENTS,
93     HY_PKG_UPGRADABLE,
94     HY_PKG_UPGRADES,
95     HY_PKG_UPGRADES_BY_PRIORITY,
96     HY_PKG_URL,
97     HY_PKG_VERSION
98 };
99 
100 static const char * const keyname_char_matches[] = {
101     "pkg",
102     "advisory",
103     "advisory_bug",
104     "advisory_cve",
105     "advisory_severity",
106     "advisory_type",
107     "arch",
108     "conflicts",
109     "description",
110     "downgradable",
111     "downgrades",
112     "empty",
113     "enhances",
114     "epoch",
115     "evr",
116     "file",
117     "latest",
118     "latest_per_arch",
119     "latest_per_arch_by_priority",
120     "location",
121     "name",
122     "nevra",
123     "nevra_strict",
124     "obsoletes",
125     "obsoletes_by_priority",
126     "provides",
127     "recommends",
128     "release",
129     "reponame",
130     "requires",
131     "sourcerpm",
132     "suggests",
133     "summary",
134     "supplements",
135     "upgradable",
136     "upgrades",
137     "upgrades_by_priority",
138     "url",
139     "version",
140     NULL
141 };
142 
143 static const char * const query_cmp_map_char[] = {
144     "eq",
145     "gt",
146     "lt",
147     "neq",
148     "not",
149     "gte",
150     "lte",
151     "substr",
152     "glob",
153     "eqg",
154     "upgrade",
155     NULL
156 };
157 
158 static const int query_cmp_map_int[] = {
159     HY_EQ,
160     HY_GT,
161     HY_LT,
162     HY_NEQ,
163     HY_NOT,
164     HY_EQ | HY_GT,
165     HY_EQ | HY_LT,
166     HY_SUBSTR,
167     HY_GLOB,
168     HY_EQG,
169     HY_UPGRADE
170 };
171 
172 HyQuery
queryFromPyObject(PyObject * o)173 queryFromPyObject(PyObject *o)
174 {
175     if (!PyType_IsSubtype(o->ob_type, &query_Type)) {
176         PyErr_SetString(PyExc_TypeError, "Expected a Query object.");
177         return NULL;
178     }
179     return ((_QueryObject *)o)->query;
180 }
181 
182 PyObject *
queryToPyObject(HyQuery query,PyObject * sack,PyTypeObject * custom_object_type)183 queryToPyObject(HyQuery query, PyObject *sack, PyTypeObject *custom_object_type)
184 {
185     _QueryObject *self = (_QueryObject *)custom_object_type->tp_alloc(custom_object_type, 0);
186     if (self) {
187         self->query = query;
188         self->sack = sack;
189         Py_INCREF(sack);
190     }
191     return (PyObject *) self;
192 }
193 
194 int
query_converter(PyObject * o,HyQuery * query_ptr)195 query_converter(PyObject *o, HyQuery *query_ptr)
196 {
197     HyQuery query = queryFromPyObject(o);
198     if (query == NULL)
199         return 0;
200     *query_ptr = query;
201     return 1;
202 }
203 
204 /* functions on the type */
205 
206 static PyObject *
query_new(PyTypeObject * type,PyObject * args,PyObject * kwds)207 query_new(PyTypeObject *type, PyObject *args, PyObject *kwds) try
208 {
209     _QueryObject *self = (_QueryObject *)type->tp_alloc(type, 0);
210     if (self) {
211         self->query = NULL;
212         self->sack = NULL;
213     }
214     return (PyObject *)self;
215 } CATCH_TO_PYTHON
216 
217 static void
query_dealloc(_QueryObject * self)218 query_dealloc(_QueryObject *self)
219 {
220     if (self->query)
221         delete self->query;
222     Py_XDECREF(self->sack);
223     Py_TYPE(self)->tp_free(self);
224 }
225 
226 static int
query_init(_QueryObject * self,PyObject * args,PyObject * kwds)227 query_init(_QueryObject * self, PyObject *args, PyObject *kwds) try
228 {
229     const char *kwlist[] = {"sack", "flags", "query", NULL};
230     PyObject *sack = NULL;
231     PyObject *query = NULL;
232     int flags = 0;
233 
234     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OiO", (char**) kwlist, &sack, &flags, &query))
235         return -1;
236 
237     if (query && (!sack || sack == Py_None) && queryObject_Check(query)) {
238         _QueryObject *query_obj = (_QueryObject*)query;
239         self->sack = query_obj->sack;
240         self->query = new libdnf::Query(*query_obj->query);
241     } else if (sack && (!query || query == Py_None) && sackObject_Check(sack)) {
242         DnfSack *csack = sackFromPyObject(sack);
243         assert(csack);
244         self->sack = sack;
245         self->query = new libdnf::Query(csack, static_cast<libdnf::Query::ExcludeFlags>(flags));
246     } else {
247         const char *msg = "Expected a _hawkey.Sack or a _hawkey.Query object.";
248         PyErr_SetString(PyExc_TypeError, msg);
249         return -1;
250     }
251     Py_INCREF(self->sack);
252     return 0;
253 } CATCH_TO_PYTHON_INT
254 
255 /* object attributes */
256 
257 static PyObject *
get_evaluated(_QueryObject * self,void * unused)258 get_evaluated(_QueryObject *self, void *unused) try
259 {
260     HyQuery q = self->query;
261     return PyBool_FromLong((long) q->getApplied());
262 } CATCH_TO_PYTHON
263 
264 static PyObject *
clear(_QueryObject * self,PyObject * unused)265 clear(_QueryObject *self, PyObject *unused) try
266 {
267     self->query->clear();
268     Py_RETURN_NONE;
269 } CATCH_TO_PYTHON
270 
271 static int
raise_bad_filter(void)272 raise_bad_filter(void)
273 {
274     PyErr_SetString(HyExc_Query, "Invalid filter key or match type.");
275     return 0;
276 }
277 
278 static int
filter_add(HyQuery query,key_t keyname,int cmp_type,PyObject * match)279 filter_add(HyQuery query, key_t keyname, int cmp_type, PyObject *match)
280 {
281     if (keyname == HY_PKG_DOWNGRADABLE ||
282         keyname == HY_PKG_DOWNGRADES ||
283         keyname == HY_PKG_EMPTY ||
284         keyname == HY_PKG_LATEST_PER_ARCH ||
285         keyname == HY_PKG_LATEST_PER_ARCH_BY_PRIORITY ||
286         keyname == HY_PKG_LATEST ||
287         keyname == HY_PKG_UPGRADABLE ||
288         keyname == HY_PKG_UPGRADES ||
289         keyname == HY_PKG_UPGRADES_BY_PRIORITY) {
290         int val;
291 
292         if (!PyInt_Check(match) || cmp_type != HY_EQ) {
293             PyErr_SetString(HyExc_Value, "Invalid boolean filter query.");
294             return 0;
295         }
296         long val_long = PyLong_AsLong(match);
297         if (val_long < INT_MIN) {
298             val = INT_MIN;
299         } else if (val_long > INT_MAX) {
300             val = INT_MAX;
301         } else {
302             val = val_long;
303         }
304         if (keyname == HY_PKG_EMPTY) {
305             if (!val) {
306                 PyErr_SetString(HyExc_Value, "Invalid boolean filter query.");
307                 return 0;
308             }
309             query->addFilter(HY_PKG_EMPTY, HY_EQ, 1);
310         } else {
311             query->addFilter(keyname, HY_EQ, val);
312         }
313         return 1;
314     }
315     if (PyUnicode_Check(match) || PyString_Check(match)) {
316         PycompString cmatch(match);
317         if (!cmatch.getCString())
318             return 0;
319         int query_filter_ret = query->addFilter(keyname, cmp_type, cmatch.getCString());
320 
321         if (query_filter_ret)
322             return raise_bad_filter();
323         return 1;
324     }
325     if (PyInt_Check(match)) {
326         long val = PyLong_AsLong(match);
327         if (cmp_type == HY_GLOB) // Workaround: Python can send integer with HY_GLOB
328             cmp_type = HY_EQ;
329         if (val > INT_MAX || val < INT_MIN) {
330             PyErr_SetString(HyExc_Value, "Numeric argument out of range.");
331             return 0;
332         }
333         if (query->addFilter(keyname, cmp_type, val))
334             return raise_bad_filter();
335         return 1;
336     }
337     if (queryObject_Check(match)) {
338         HyQuery target = queryFromPyObject(match);
339         const DnfPackageSet * pset = target->runSet();
340         int ret = query->addFilter(keyname, cmp_type, pset);
341 
342         if (ret)
343             return raise_bad_filter();
344         return 1;
345     }
346     if (reldepObject_Check(match)) {
347         DnfReldep *reldep = reldepFromPyObject(match);
348         if (cmp_type != HY_EQ || query->addFilter(keyname, reldep))
349             return raise_bad_filter();
350         return 1;
351     }
352     // match is a sequence now:
353     switch (keyname) {
354     case HY_PKG:
355     case HY_PKG_OBSOLETES:
356     case HY_PKG_OBSOLETES_BY_PRIORITY:
357     case HY_PKG_CONFLICTS:
358     case HY_PKG_REQUIRES:
359     case HY_PKG_ENHANCES:
360     case HY_PKG_RECOMMENDS:
361     case HY_PKG_SUGGESTS:
362     case HY_PKG_SUPPLEMENTS: {
363         // It could be a sequence of packages or reldep/strings. Lets try packages first.
364         auto pset = pyseq_to_packageset(match, query->getSack());
365         if (!pset) {
366             if (auto PyError = PyErr_Occurred()) {
367                 // It was not a sequence of packages.
368                 if (PyErr_GivenExceptionMatches(PyError, PyExc_TypeError)) {
369                     PyErr_Clear();
370                     auto reldeplist = pyseq_to_reldeplist(match, query->getSack(), cmp_type);
371                     if (reldeplist == NULL)
372                         return 1;
373 
374                     int ret = query->addFilter(keyname, reldeplist.get());
375                     if (ret) {
376                         return raise_bad_filter();
377                     }
378                     break;
379                 }
380             }
381             return 1;
382         }
383         int ret = query->addFilter(keyname, cmp_type, pset.get());
384         if (ret)
385             return raise_bad_filter();
386 
387         break;
388     }
389     case HY_PKG_PROVIDES: {
390         auto reldeplist = pyseq_to_reldeplist(match, query->getSack(), cmp_type);
391         if (reldeplist == NULL)
392             return 1;
393 
394         int ret = query->addFilter(keyname, reldeplist.get());
395         if (ret)
396             return raise_bad_filter();
397         break;
398     }
399     default: {
400         std::vector<std::string> matches;
401         try {
402             matches = pySequenceConverter(match);
403         } catch (std::runtime_error &) {
404             return 0;
405         }
406         std::vector<const char *> matchesCString(matches.size() + 1);
407         std::transform(matches.begin(), matches.end(), matchesCString.begin(),
408             std::mem_fn(&std::string::c_str));
409         int filter_in_ret = query->addFilter(keyname, cmp_type, matchesCString.data());
410         if (filter_in_ret)
411             return raise_bad_filter();
412         break;
413     }
414     }
415     return 1;
416 }
417 
418 static char *
filter_key_splitter(char ** key)419 filter_key_splitter(char** key)
420 {
421     char *sbegin = *key;
422 	char *end;
423 
424     if (sbegin == NULL)
425 		return NULL;
426     int index;
427 
428     for (index = 0; sbegin[index] != '\0'; ++index) {
429         if ((sbegin[index] == '_') &&  (sbegin[index + 1] == '_')) {
430             end = sbegin + index;
431             *end++ = '\0';
432             *key = ++end;
433             return sbegin;
434         }
435     }
436     *key = NULL;
437     return sbegin;
438 }
439 
440 gboolean
filter_internal(HyQuery query,HySelector sltr,PyObject * sack,PyObject * args,PyObject * kwds)441 filter_internal(HyQuery query, HySelector sltr, PyObject *sack, PyObject *args, PyObject *kwds)
442 {
443     PyObject *key, *value;
444     Py_ssize_t pos = 0;
445     key_t keyname;
446     int cmp_type;
447     PyObject *tuple_item;
448     int argument_number, presence_cmp_type;
449     int cmp_type_flag = 0;
450 
451     if (args != NULL) {
452         Py_ssize_t tuple_size = PyTuple_Size(args);
453         for (int x = 0; x < tuple_size; ++x) {
454             tuple_item = PyTuple_GetItem(args, x);
455             if (PyInt_Check(tuple_item)) {
456                 long c_int = PyLong_AsLong(tuple_item);
457                 if (c_int == HY_ICASE) {
458                     cmp_type_flag = HY_ICASE;
459                 } else {
460                     PyErr_SetString(HyExc_Value, "Invalid flag. Only HY_ICASE allowed");
461                     return FALSE;
462                 }
463             }
464         }
465     }
466 
467     if (kwds != NULL) {
468         while (PyDict_Next(kwds, &pos, &key, &value)) {
469             keyname = -1;
470             argument_number = 0;
471             PycompString cmatch(key);
472             if (!cmatch.getCString())
473                 return FALSE;
474             auto parsed_string = cmatch.getString();
475             char *tmp_string = &parsed_string.front();
476             cmp_type = 0;
477             char *parcial_string;
478             while ((parcial_string = filter_key_splitter(&tmp_string)) != NULL) {
479                 if (!argument_number) {
480                     for (unsigned int i = 0; keyname_char_matches[i] != NULL; ++i) {
481                         if (strcmp(keyname_char_matches[i], parcial_string) == 0) {
482                             keyname = keyname_int_matches[i];
483                             argument_number = 1;
484                             break;
485                         }
486                     }
487                     if (!argument_number) {
488                         PyErr_SetString(HyExc_Value, g_strdup_printf(
489                             "Unrecognized key name: %s", parcial_string));
490                         return FALSE;
491                     }
492                 } else {
493                     presence_cmp_type = FALSE;
494                     for (unsigned int i = 0; query_cmp_map_char[i] != NULL; ++i) {
495                         if (strcmp(query_cmp_map_char[i], parcial_string) == 0) {
496                             cmp_type |= query_cmp_map_int[i];
497                             presence_cmp_type = TRUE;
498                             break;
499                         }
500                     }
501                     if (!presence_cmp_type) {
502                         PyErr_SetString(HyExc_Value, g_strdup_printf(
503                             "Unrecognized filter type: %s", parcial_string));
504                         return FALSE;
505                     }
506                 }
507             }
508             if (cmp_type == 0) {
509                 cmp_type = HY_EQ;
510             }
511             if (keyname != -1) {
512                 if (query != NULL) {
513                     if (filter_add(query, keyname, cmp_type|cmp_type_flag, value) == 0) {
514                         return FALSE;
515                     }
516                 } else {
517                     if (keyname == HY_PKG) {
518                         DnfSack *c_sack = sackFromPyObject(sack);
519                         assert(c_sack);
520                         auto pset = pyseq_to_packageset(value, c_sack);
521                         if (!pset) {
522                             ret2e(DNF_ERROR_BAD_SELECTOR, "Invalid value type: Only List and Query supported");
523                             return FALSE;
524                         }
525                         if (!sltr) {
526                             PyErr_SetString(HyExc_Value, "Selector is nulptr");
527                             return FALSE;
528                         }
529                         if (ret2e(sltr->set(pset.get()),
530                             "Invalid Selector spec." )) {
531                             return FALSE;
532                         }
533                     } else {
534                         PycompString c_sltr_match(value);
535                         if (!c_sltr_match.getCString())
536                             return FALSE;
537                         if (ret2e(hy_selector_set(sltr, keyname, cmp_type, c_sltr_match.getCString()),
538                             "Invalid Selector spec." )) {
539                             return FALSE;
540                         }
541                     }
542                 }
543             }
544         }
545     }
546     return TRUE;
547 }
548 
549 static PyObject *
filter(_QueryObject * self,PyObject * args,PyObject * kwds)550 filter(_QueryObject *self, PyObject *args, PyObject *kwds) try {
551     auto query = std::unique_ptr<libdnf::Query>(new libdnf::Query(*self->query));
552     gboolean ret = filter_internal(query.get(), NULL, self->sack, args, kwds);
553     if (!ret)
554         return NULL;
555     PyObject *final_query = queryToPyObject(query.release(), self->sack, Py_TYPE(self));
556     return final_query;
557 } CATCH_TO_PYTHON
558 
559 static _QueryObject *
filterm(_QueryObject * self,PyObject * args,PyObject * kwds)560 filterm(_QueryObject *self, PyObject *args, PyObject *kwds) try {
561     gboolean ret = filter_internal(self->query, NULL, self->sack, args, kwds);
562     if (!ret)
563         return NULL;
564     Py_INCREF(self);
565     return self;
566 } CATCH_TO_PYTHON
567 
568 static PyObject *
add_available_filter(_QueryObject * self,PyObject * unused)569 add_available_filter(_QueryObject *self, PyObject *unused) try
570 {
571     HyQuery query = new libdnf::Query(*self->query);
572     query->available();
573     PyObject *final_query = queryToPyObject(query, self->sack, Py_TYPE(self));
574     return final_query;
575 } CATCH_TO_PYTHON
576 
577 static PyObject *
add_downgrades_filter(_QueryObject * self,PyObject * unused)578 add_downgrades_filter(_QueryObject *self, PyObject *unused) try
579 {
580     HyQuery query = new libdnf::Query(*self->query);
581     query->addFilter(HY_PKG_DOWNGRADES, HY_EQ, 1);
582     PyObject *final_query = queryToPyObject(query, self->sack, Py_TYPE(self));
583     return final_query;
584 } CATCH_TO_PYTHON
585 
586 static PyObject *
duplicated_filter(_QueryObject * self,PyObject * unused)587 duplicated_filter(_QueryObject *self, PyObject *unused) try
588 {
589     HyQuery self_query_copy = new libdnf::Query(*self->query);
590     self_query_copy->filterDuplicated();
591     PyObject *final_query = queryToPyObject(self_query_copy, self->sack, Py_TYPE(self));
592     return final_query;
593 } CATCH_TO_PYTHON
594 
595 static PyObject *
add_filter_extras(_QueryObject * self,PyObject * unused)596 add_filter_extras(_QueryObject *self, PyObject *unused) try
597 {
598     HyQuery self_query_copy = new libdnf::Query(*self->query);
599     self_query_copy->filterExtras();
600     PyObject *final_query = queryToPyObject(self_query_copy, self->sack, Py_TYPE(self));
601     return final_query;
602 } CATCH_TO_PYTHON
603 
604 static PyObject *
add_installed_filter(_QueryObject * self,PyObject * unused)605 add_installed_filter(_QueryObject *self, PyObject *unused) try
606 {
607     HyQuery query = new libdnf::Query(*self->query);
608     query->installed();
609     PyObject *final_query = queryToPyObject(query, self->sack, Py_TYPE(self));
610     return final_query;
611 } CATCH_TO_PYTHON
612 
613 static PyObject *
add_filter_latest(_QueryObject * self,PyObject * args)614 add_filter_latest(_QueryObject *self, PyObject *args) try
615 {
616     int value = 1;
617 
618     if (!PyArg_ParseTuple(args, "|i", &value))
619         return NULL;
620 
621     HyQuery query = new libdnf::Query(*self->query);
622     query->addFilter(HY_PKG_LATEST_PER_ARCH, HY_EQ, value);
623     PyObject *final_query = queryToPyObject(query, self->sack, Py_TYPE(self));
624     return final_query;
625 } CATCH_TO_PYTHON
626 
627 static PyObject *
add_upgrades_filter(_QueryObject * self,PyObject * unused)628 add_upgrades_filter(_QueryObject *self, PyObject *unused) try
629 {
630     HyQuery query = new libdnf::Query(*self->query);
631     query->addFilter(HY_PKG_UPGRADES, HY_EQ, 1);
632     PyObject *final_query = queryToPyObject(query, self->sack, Py_TYPE(self));
633     return final_query;
634 } CATCH_TO_PYTHON
635 
636 
637 static PyObject *
run(_QueryObject * self,PyObject * unused)638 run(_QueryObject *self, PyObject *unused) try
639 {
640     PyObject *list;
641 
642     const DnfPackageSet * pset = self->query->runSet();
643     list = packageset_to_pylist(pset, self->sack);
644     return list;
645 } CATCH_TO_PYTHON
646 
647 static PyObject *
apply(PyObject * self,PyObject * unused)648 apply(PyObject *self, PyObject *unused) try
649 {
650     ((_QueryObject *) self)->query->apply();
651     Py_INCREF(self);
652     return self;
653 } CATCH_TO_PYTHON
654 
655 static PyObject *
q_union(PyObject * self,PyObject * args)656 q_union(PyObject *self, PyObject *args) try
657 {
658     PyObject *other;
659     if (!PyArg_ParseTuple(args, "O!", &query_Type, &other))
660             return NULL;
661 
662     HyQuery self_query_copy = new libdnf::Query(*((_QueryObject *) self)->query);
663     HyQuery other_q = ((_QueryObject *) other)->query;
664     self_query_copy->queryUnion(*other_q);
665     PyObject *final_query = queryToPyObject(self_query_copy, ((_QueryObject *) self)->sack,
666                                             Py_TYPE(self));
667     return final_query;
668 } CATCH_TO_PYTHON
669 
670 static PyObject *
q_intersection(PyObject * self,PyObject * args)671 q_intersection(PyObject *self, PyObject *args) try
672 {
673     PyObject *other;
674     if (!PyArg_ParseTuple(args, "O!", &query_Type, &other))
675             return NULL;
676 
677     HyQuery self_query_copy = new libdnf::Query(*((_QueryObject *) self)->query);
678     HyQuery other_q = ((_QueryObject *) other)->query;
679     self_query_copy->queryIntersection(*other_q);
680     PyObject *final_query = queryToPyObject(self_query_copy, ((_QueryObject *) self)->sack,
681                                             Py_TYPE(self));
682     return final_query;
683 } CATCH_TO_PYTHON
684 
685 static PyObject *
q_difference(PyObject * self,PyObject * args)686 q_difference(PyObject *self, PyObject *args) try
687 {
688     PyObject *other;
689     if (!PyArg_ParseTuple(args, "O!", &query_Type, &other))
690             return NULL;
691 
692     HyQuery self_query_copy = new libdnf::Query(*((_QueryObject *) self)->query);
693     HyQuery other_q = ((_QueryObject *) other)->query;
694     self_query_copy->queryDifference(*other_q);
695     PyObject *final_query = queryToPyObject(self_query_copy, ((_QueryObject *) self)->sack,
696                                             Py_TYPE(self));
697     return final_query;
698 } CATCH_TO_PYTHON
699 
700 typedef struct {
701     PyObject_HEAD
702     libdnf::Swdb *ptr;
703     void *ty;
704     int own;
705     PyObject *next;
706 } SwdbSwigPyObject;
707 
708 static PyObject *
get_advisory_pkgs(_QueryObject * self,PyObject * args)709 get_advisory_pkgs(_QueryObject *self, PyObject *args) try
710 {
711     int cmpType;
712 
713     if (!PyArg_ParseTuple(args, "i", &cmpType))
714         return NULL;
715 
716     std::vector<libdnf::AdvisoryPkg> advisoryPkgs;
717 
718     self->query->getAdvisoryPkgs(cmpType, advisoryPkgs);
719     return advisoryPkgVectorToPylist(advisoryPkgs);
720 } CATCH_TO_PYTHON
721 
722 static PyObject *
filter_userinstalled(PyObject * self,PyObject * args,PyObject * kwds)723 filter_userinstalled(PyObject *self, PyObject *args, PyObject *kwds) try
724 {
725     const char *kwlist[] = {"swdb", NULL};
726     PyObject *pySwdb;
727 
728     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", (char **)kwlist, &pySwdb)) {
729         return NULL;
730     }
731 
732     UniquePtrPyObject thisPySwdb(PyObject_GetAttrString(pySwdb, "this"));
733     auto swigSwdb = reinterpret_cast< SwdbSwigPyObject * >(thisPySwdb.get());
734 
735     if (swigSwdb == nullptr) {
736         PyErr_SetString(PyExc_SystemError, "Unable to parse SwigPyObject");
737         return NULL;
738     }
739 
740     libdnf::Swdb * swdb = swigSwdb->ptr;
741 
742     if (swdb == NULL) {
743         PyErr_SetString(PyExc_SystemError, "Unable to parse swig object");
744         return NULL;
745     }
746     HyQuery self_query_copy = new libdnf::Query(*((_QueryObject *) self)->query);
747     self_query_copy->filterUserInstalled(*swdb);
748 
749     PyObject *final_query = queryToPyObject(self_query_copy, ((_QueryObject *) self)->sack,
750                                             Py_TYPE(self));
751     return final_query;
752 } CATCH_TO_PYTHON
753 
754 static PyObject *
filter_unneeded_or_safe_to_remove(PyObject * self,PyObject * args,PyObject * kwds,bool SafeToRemove)755 filter_unneeded_or_safe_to_remove(PyObject *self, PyObject *args, PyObject *kwds, bool SafeToRemove)
756 {
757     const char *kwlist[] = {"swdb", "debug_solver", NULL};
758     PyObject *pySwdb;
759     PyObject *debug_solver = NULL;
760 
761     if (!PyArg_ParseTupleAndKeywords(
762             args, kwds, "O|O!", (char **)kwlist, &pySwdb, &PyBool_Type, &debug_solver)) {
763         return NULL;
764     }
765 
766     UniquePtrPyObject thisPySwdb(PyObject_GetAttrString(pySwdb, "this"));
767     auto swigSwdb = reinterpret_cast< SwdbSwigPyObject * >(thisPySwdb.get());
768 
769     if (swigSwdb == nullptr) {
770         PyErr_SetString(PyExc_SystemError, "Unable to parse SwigPyObject");
771         return NULL;
772     }
773 
774     libdnf::Swdb *swdb = swigSwdb->ptr;
775 
776     if (swdb == NULL) {
777         PyErr_SetString(PyExc_SystemError, "Unable to parse swig object");
778         return NULL;
779     }
780     std::unique_ptr<libdnf::Query> self_query_copy(new libdnf::Query(*((_QueryObject *) self)->query));
781     gboolean c_debug_solver = debug_solver != NULL && PyObject_IsTrue(debug_solver);
782 
783     int ret;
784     if (SafeToRemove) {
785         ret = self_query_copy->filterSafeToRemove(*swdb, c_debug_solver);
786     } else {
787         ret = self_query_copy->filterUnneeded(*swdb, c_debug_solver);
788     }
789     if (ret == -1) {
790         PyErr_SetString(PyExc_SystemError, "Unable to provide query with unneded filter");
791         return NULL;
792     }
793 
794     PyObject *final_query = queryToPyObject(self_query_copy.release(), ((_QueryObject *) self)->sack,
795                                             Py_TYPE(self));
796     return final_query;
797 }
798 
799 static PyObject *
filter_safe_to_remove(PyObject * self,PyObject * args,PyObject * kwds)800 filter_safe_to_remove(PyObject *self, PyObject *args, PyObject *kwds) try
801 {
802     return filter_unneeded_or_safe_to_remove(self, args, kwds, true);
803 } CATCH_TO_PYTHON
804 
805 
806 static PyObject *
filter_unneeded(PyObject * self,PyObject * args,PyObject * kwds)807 filter_unneeded(PyObject *self, PyObject *args, PyObject *kwds) try
808 {
809     return filter_unneeded_or_safe_to_remove(self, args, kwds, false);
810 } CATCH_TO_PYTHON
811 
812 static PyObject *
q_add(_QueryObject * self,PyObject * list)813 q_add(_QueryObject *self, PyObject *list) try
814 {
815     if (!PyList_Check(list)) {
816         PyErr_SetString(PyExc_TypeError, "Only a list can be concatenated to a Query");
817         return NULL;
818     }
819     PyObject *unused = NULL;
820     PyObject *query_list = run(self, unused);
821 
822     int list_count = PyList_Size(list);
823     for (int index = 0; index < list_count; ++index)
824         PyList_Append(query_list, PyList_GetItem(list, index));
825     return query_list;
826 } CATCH_TO_PYTHON
827 
828 static int
query_contains(PyObject * self,PyObject * pypkg)829 query_contains(PyObject *self, PyObject *pypkg) try
830 {
831     HyQuery q = ((_QueryObject *) self)->query;
832     DnfPackage *pkg = packageFromPyObject(pypkg);
833 
834     if (pkg) {
835         Id id = dnf_package_get_id(pkg);
836         q->apply();
837         if (MAPTST(q->getResult(), id))
838             return 1;
839     }
840     return 0;
841 } CATCH_TO_PYTHON_INT
842 
843 static size_t
query_len(PyObject * self)844 query_len(PyObject *self) try
845 {
846     HyQuery q = ((_QueryObject *) self)->query;
847     return q->size();
848 } CATCH_TO_PYTHON_INT
849 
850 static PyObject *
q_length(PyObject * self,PyObject * unused)851 q_length(PyObject *self, PyObject *unused) try
852 {
853     return PyLong_FromLong(query_len(self));
854 } CATCH_TO_PYTHON
855 
856 static PyObject *
query_get_item(PyObject * self,int index)857 query_get_item(PyObject *self, int index) try
858 {
859     HyQuery query = ((_QueryObject *) self)->query;
860     Id id = query->getIndexItem(index);
861     if (id == -1) {
862         PyErr_SetString(PyExc_IndexError, "list index out of range");
863         return NULL;
864     }
865     PyObject *package = new_package(((_QueryObject *) self)->sack, id);
866     return package;
867 } CATCH_TO_PYTHON
868 
869 static PyObject *
query_iter(PyObject * self)870 query_iter(PyObject *self) try
871 {
872     const DnfPackageSet * pset = ((_QueryObject *) self)->query->runSet();
873     UniquePtrPyObject list(packageset_to_pylist(pset, ((_QueryObject *) self)->sack));
874     if (!list)
875         return NULL;
876     PyObject *iter = PyObject_GetIter(list.get());
877     return iter;
878 } CATCH_TO_PYTHON
879 
880 static PyObject *
query_to_name_dict(_QueryObject * self,PyObject * unused)881 query_to_name_dict(_QueryObject *self, PyObject *unused) try
882 {
883     HyQuery query = ((_QueryObject *) self)->query;
884     Pool *pool = dnf_sack_get_pool(query->getSack());
885 
886     libdnf::IdQueue samename;
887     hy_query_to_name_ordered_queue(query, &samename);
888 
889     Solvable *considered;
890     Id name = 0;
891     UniquePtrPyObject list(PyList_New(0));
892     UniquePtrPyObject ret_dict(PyDict_New());
893 
894     for (int i = 0; i < samename.size(); ++i) {
895         Id package_id = samename[i];
896         considered = pool->solvables + package_id;
897         if (name == 0) {
898             name = considered->name;
899         } else if (name != considered->name) {
900             PyDict_SetItemString(ret_dict.get(), pool_id2str(pool, name), list.get());
901             list.reset(PyList_New(0));
902             name = considered->name;
903         }
904         UniquePtrPyObject package(new_package(self->sack, package_id));
905         if (!package)
906             goto fail;
907 
908         int rc = PyList_Append(list.get(), package.get());
909         if (rc == -1)
910             goto fail;
911     }
912     if (name)
913         PyDict_SetItemString(ret_dict.get(), pool_id2str(pool, name), list.get());
914     return ret_dict.release();
915 
916     fail:
917         PyErr_SetString(PyExc_SystemError, "Unable to create name_dict");
918         return NULL;
919 } CATCH_TO_PYTHON
920 
921 static PyObject *
query_to_name_arch_dict(_QueryObject * self,PyObject * unused)922 query_to_name_arch_dict(_QueryObject *self, PyObject *unused) try
923 {
924     HyQuery query = ((_QueryObject *) self)->query;
925     Pool *pool = dnf_sack_get_pool(query->getSack());
926 
927     libdnf::IdQueue samename;
928 
929     hy_query_to_name_arch_ordered_queue(query, &samename);
930 
931     Solvable *considered;
932     Id name = 0;
933     Id arch = 0;
934     UniquePtrPyObject key(PyTuple_New(2));
935     UniquePtrPyObject list(PyList_New(0));
936     UniquePtrPyObject ret_dict(PyDict_New());
937 
938     for (int i = 0; i < samename.size(); ++i) {
939         Id package_id = samename[i];
940         considered = pool->solvables + package_id;
941         if (name == 0) {
942             name = considered->name;
943             arch = considered->arch;
944         } else if ((name != considered->name) || (arch != considered->arch)) {
945             if (PyTuple_SetItem(key.get(), 0, PyString_FromString(pool_id2str(pool, name))))
946                 goto fail;
947             if (PyTuple_SetItem(key.get(), 1, PyString_FromString(pool_id2str(pool, arch))))
948                 goto fail;
949             PyDict_SetItem(ret_dict.get(), key.get(), list.get());
950             key.reset(PyTuple_New(2));
951             list.reset(PyList_New(0));
952             name = considered->name;
953             arch = considered->arch;
954         }
955         UniquePtrPyObject package(new_package(self->sack, package_id));
956         if (!package)
957             goto fail;
958 
959         int rc = PyList_Append(list.get(), package.get());
960         if (rc == -1)
961             goto fail;
962     }
963     if (name) {
964         if (PyTuple_SetItem(key.get(), 0, PyString_FromString(pool_id2str(pool, name))))
965             goto fail;
966         if (PyTuple_SetItem(key.get(), 1, PyString_FromString(pool_id2str(pool, arch))))
967             goto fail;
968         PyDict_SetItem(ret_dict.get(), key.get(), list.get());
969     }
970 
971     return ret_dict.release();
972 
973     fail:
974         PyErr_SetString(PyExc_SystemError, "Unable to create name_arch_dict");
975         return NULL;
976 } CATCH_TO_PYTHON
977 
978 static PyObject *
add_nevra_or_other_filter(_QueryObject * self,PyObject * args)979 add_nevra_or_other_filter(_QueryObject *self, PyObject *args) try
980 {
981     auto self_query_copy = std::unique_ptr<libdnf::Query>(new libdnf::Query(*self->query));
982 
983     int arguments_count = PyTuple_Size(args);
984     if (arguments_count == 1) {
985         const char *name;
986         if (!PyArg_ParseTuple(args, "s", &name))
987             return NULL;
988         libdnf::Nevra nevra;
989         if (nevra.parse(name, HY_FORM_NEVRA))
990             self_query_copy->addFilter(&nevra, false);
991         else
992             self_query_copy->addFilter(HY_PKG_EMPTY, HY_EQ, 1);
993     } else if (arguments_count == 3) {
994         const char *name;
995         const char *evr;
996         const char *arch;
997 
998         if (!PyArg_ParseTuple(args, "sss", &name, &evr, &arch))
999             return NULL;
1000         self_query_copy->addFilter(HY_PKG_NAME, HY_EQ, name);
1001         self_query_copy->addFilter(HY_PKG_EVR, HY_EQ, evr);
1002         self_query_copy->addFilter(HY_PKG_ARCH, HY_EQ, arch);
1003     } else {
1004         PyErr_SetString(PyExc_TypeError,
1005                         "nevra() takes 1 (NEVRA), or 3 (name, evr, arch) str params");
1006         return NULL;
1007     }
1008     PyObject *final_query = queryToPyObject(self_query_copy.release(), self->sack, Py_TYPE(self));
1009     return final_query;
1010 } CATCH_TO_PYTHON
1011 
1012 static PyObject *
add_filter_recent(_QueryObject * self,PyObject * args)1013 add_filter_recent(_QueryObject *self, PyObject *args) try
1014 {
1015     long recent;
1016     if (!PyArg_ParseTuple(args, "l", &recent))
1017         return NULL;
1018 
1019     self->query->apply();
1020     HyQuery self_query_copy = new libdnf::Query(*self->query);
1021     time_t now = time(NULL);
1022     time_t recent_limit = now - (recent*86400);
1023     self_query_copy->filterRecent((recent_limit < 0) ? 0 : recent_limit);
1024     PyObject *final_query = queryToPyObject(self_query_copy, self->sack, Py_TYPE(self));
1025     return final_query;
1026 } CATCH_TO_PYTHON
1027 
1028 static PyGetSetDef query_getsetters[] = {
1029     {(char*)"evaluated",  (getter)get_evaluated, NULL, NULL, NULL},
1030     {NULL}                        /* sentinel */
1031 };
1032 
1033 PySequenceMethods query_sequence = {
1034     (lenfunc)query_len,               /* sq_length */
1035     (binaryfunc)q_add,                /* sq_concat */
1036     0,                                /* sq_repeat */
1037     (ssizeargfunc) query_get_item,    /* sq_item */
1038     0,                                /* sq_slice */
1039     0,                                /* sq_ass_item */
1040     0,                                /* sq_ass_slice */
1041     (objobjproc)query_contains,       /* sq_contains */
1042 };
1043 
1044 static struct PyMethodDef query_methods[] = {
1045     {"clear", (PyCFunction)clear, METH_NOARGS,
1046      NULL},
1047     {"filter", (PyCFunction)filter, METH_KEYWORDS|METH_VARARGS,
1048      NULL},
1049     {"filterm", (PyCFunction)filterm, METH_KEYWORDS|METH_VARARGS,
1050      NULL},
1051     {"run", (PyCFunction)run, METH_NOARGS,
1052      NULL},
1053     {"apply", (PyCFunction)apply, METH_NOARGS,
1054      NULL},
1055     {"available", (PyCFunction)add_available_filter, METH_NOARGS, NULL},
1056     {"downgrades", (PyCFunction)add_downgrades_filter, METH_NOARGS, NULL},
1057     {"duplicated", (PyCFunction)duplicated_filter, METH_NOARGS, NULL},
1058     {"extras", (PyCFunction)add_filter_extras, METH_NOARGS, NULL},
1059     {"installed", (PyCFunction)add_installed_filter, METH_NOARGS, NULL},
1060     {"latest", (PyCFunction)add_filter_latest, METH_VARARGS, NULL},
1061     {"union", (PyCFunction)q_union, METH_VARARGS, NULL},
1062     {"upgrades", (PyCFunction)add_upgrades_filter, METH_NOARGS, NULL},
1063     {"intersection", (PyCFunction)q_intersection, METH_VARARGS, NULL},
1064     {"difference", (PyCFunction)q_difference, METH_VARARGS, NULL},
1065     {"count", (PyCFunction)q_length, METH_NOARGS,
1066         NULL},
1067     {"get_advisory_pkgs", (PyCFunction)get_advisory_pkgs, METH_VARARGS, NULL},
1068     {"userinstalled", (PyCFunction)filter_userinstalled, METH_KEYWORDS|METH_VARARGS, NULL},
1069     {"_na_dict", (PyCFunction)query_to_name_arch_dict, METH_NOARGS, NULL},
1070     {"_name_dict", (PyCFunction)query_to_name_dict, METH_NOARGS, NULL},
1071     {"_nevra", (PyCFunction)add_nevra_or_other_filter, METH_VARARGS, NULL},
1072     {"_recent", (PyCFunction)add_filter_recent, METH_VARARGS, NULL},
1073     {"_unneeded", (PyCFunction)filter_unneeded, METH_KEYWORDS|METH_VARARGS, NULL},
1074     {"_safe_to_remove", (PyCFunction)filter_safe_to_remove, METH_KEYWORDS|METH_VARARGS, NULL},
1075     {NULL}                      /* sentinel */
1076 };
1077 
1078 PyTypeObject query_Type = {
1079     PyVarObject_HEAD_INIT(NULL, 0)
1080     "_hawkey.Query",                /*tp_name*/
1081     sizeof(_QueryObject),        /*tp_basicsize*/
1082     0,                                /*tp_itemsize*/
1083     (destructor) query_dealloc, /*tp_dealloc*/
1084     0,                                /*tp_print*/
1085     0,                                /*tp_getattr*/
1086     0,                                /*tp_setattr*/
1087     0,                                /*tp_compare*/
1088     0,                                /*tp_repr*/
1089     0,                                /*tp_as_number*/
1090     &query_sequence,                  /*tp_as_sequence*/
1091     0,                                /*tp_as_mapping*/
1092     0,                                /*tp_hash */
1093     0,                                /*tp_call*/
1094     0,                                /*tp_str*/
1095     0,                                /*tp_getattro*/
1096     0,                                /*tp_setattro*/
1097     0,                                /*tp_as_buffer*/
1098     Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,        /*tp_flags*/
1099     "Query object",                /* tp_doc */
1100     0,                                /* tp_traverse */
1101     0,                                /* tp_clear */
1102     0,                                /* tp_richcompare */
1103     0,                                /* tp_weaklistoffset */
1104     query_iter,                       /* tp_iter */
1105     0,                                /* tp_iternext */
1106     query_methods,                /* tp_methods */
1107     0,                                /* tp_members */
1108     query_getsetters,                /* tp_getset */
1109     0,                                /* tp_base */
1110     0,                                /* tp_dict */
1111     0,                                /* tp_descr_get */
1112     0,                                /* tp_descr_set */
1113     0,                                /* tp_dictoffset */
1114     (initproc)query_init,        /* tp_init */
1115     0,                                /* tp_alloc */
1116     query_new,                        /* tp_new */
1117     0,                                /* tp_free */
1118     0,                                /* tp_is_gc */
1119 };
1120