/ libpkg / pkgdb_query.c
pkgdb_query.c
  1  /*-
  2   * Copyright (c) 2011-2025 Baptiste Daroussin <bapt@FreeBSD.org>
  3   * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
  4   * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
  5   * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
  6   * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
  7   * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
  8   * Copyright (c) 2012 Bryan Drewery <bryan@shatow.net>
  9   * Copyright (c) 2013 Gerald Pfeifer <gerald@pfeifer.com>
 10   * Copyright (c) 2013-2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 11   * Copyright (c) 2023 Serenity Cyber Security, LLC
 12   *                    Author: Gleb Popov <arrowd@FreeBSD.org>
 13   *
 14   * SPDX-License-Identifier: BSD-2-Clause
 15   */
 16  
 17  #include "pkg/vec.h"
 18  #ifdef HAVE_CONFIG_H
 19  #include "pkg_config.h"
 20  #endif
 21  
 22  #include <assert.h>
 23  #include <errno.h>
 24  #include <regex.h>
 25  #include <grp.h>
 26  #if __has_include(<libutil.h>)
 27  #include <libutil.h>
 28  #endif
 29  #include <stdlib.h>
 30  #include <stdio.h>
 31  #include <stdbool.h>
 32  #include <string.h>
 33  #include <unistd.h>
 34  #include <signal.h>
 35  
 36  #include <sqlite3.h>
 37  
 38  #include "pkg.h"
 39  #include "private/event.h"
 40  #include "private/pkg.h"
 41  #include "private/pkgdb.h"
 42  #include "private/utils.h"
 43  
 44  const char *
 45  pkgdb_get_pattern_query(const char *pattern, match_t match)
 46  {
 47  	char		*checkorigin = NULL;
 48  	char		*checkflavor = NULL;
 49  	const char	*comp = NULL;
 50  
 51  	if (pattern != NULL) {
 52  		checkorigin = strchr(pattern, '/');
 53  		if (checkorigin != NULL)
 54  			checkflavor = strchr(checkorigin, '@');
 55  	}
 56  
 57  	switch (match) {
 58  	case MATCH_ALL:
 59  		comp = "";
 60  		break;
 61  	case MATCH_INTERNAL:
 62  		comp = " WHERE p.name = ?1";
 63  		break;
 64  	case MATCH_EXACT:
 65  		if (pkgdb_case_sensitive()) {
 66  			if (checkorigin == NULL)
 67  				comp = " WHERE (p.name = ?1 OR p.name || '-' || version = ?1)";
 68  			else if (checkflavor == NULL)
 69  				comp = " WHERE (origin = ?1 OR categories.name || substr(origin, instr(origin, '/')) = ?1)";
 70  			else
 71  				comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor = ?1)";
 72  		} else {
 73  			if (checkorigin == NULL)
 74  				comp = " WHERE (p.name = ?1 COLLATE NOCASE OR "
 75  				"p.name || '-' || version = ?1 COLLATE NOCASE)";
 76  			else if (checkflavor == NULL)
 77  				comp = " WHERE (origin = ?1 COLLATE NOCASE OR categories.name || substr(origin, instr(origin, '/'))  = ?1 COLLATE NOCASE)";
 78  			else
 79  				comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor = ?1 COLLATE NOCASE)";
 80  		}
 81  		break;
 82  	case MATCH_GLOB:
 83  		if (pkgdb_case_sensitive()) {
 84  			if (checkorigin == NULL)
 85  				comp = " WHERE (p.name GLOB ?1 "
 86  					"OR p.name || '-' || version GLOB ?1)";
 87  			else if (checkflavor == NULL)
 88  				comp = " WHERE (origin GLOB ?1 OR categories.name || substr(origin, instr(origin, '/')) GLOB ?1)";
 89  			else
 90  				comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor GLOB ?1)";
 91  		} else  {
 92  			if (checkorigin == NULL)
 93  				comp = " WHERE (lower(p.name) GLOB lower(?1)  "
 94  					"OR lower(p.name || '-' || version) GLOB lower(?1) )";
 95  			else if (checkflavor == NULL)
 96  				comp = " WHERE (lower(origin) GLOB lower(?1) OR lower(categories.name || substr(origin, instr(origin, '/'))) GLOB lower(?1))";
 97  			else
 98  				comp = "WHERE (lower(categories.name || substr(origin, instr(origin, '/')) || '@' || flavor) GLOB lower(?1))";
 99  		}
100  		break;
101  	case MATCH_REGEX:
102  		if (checkorigin == NULL)
103  			comp = " WHERE (p.name REGEXP ?1 "
104  			    "OR p.name || '-' || version REGEXP ?1)";
105  		else if (checkflavor == NULL)
106  			comp = " WHERE (origin REGEXP ?1 OR categories.name || substr(origin, instr(origin, '/')) REGEXP ?1)";
107  		else
108  			comp = "WHERE (categories.name || substr(origin, instr(origin, '/')) || '@' || flavor REGEXP ?1)";
109  		break;
110  	}
111  
112  	return (comp);
113  }
114  
115  struct pkgdb_it *
116  pkgdb_query_cond(struct pkgdb *db, const char *cond, const char *pattern, match_t match)
117  {
118  	char		 sql[BUFSIZ];
119  	sqlite3_stmt	*stmt;
120  	const char	*comp = NULL;
121  
122  	assert(db != NULL);
123  
124  	if (match != MATCH_ALL && (pattern == NULL || pattern[0] == '\0'))
125  		return (NULL);
126  
127  	comp = pkgdb_get_pattern_query(pattern, match);
128  
129  	if (cond) {
130  		sqlite3_snprintf(sizeof(sql), sql,
131  				"WITH flavors AS "
132  				"  (SELECT package_id, value.annotation AS flavor FROM pkg_annotation "
133  				"   LEFT JOIN annotation tag ON pkg_annotation.tag_id = tag.annotation_id "
134  				"   LEFT JOIN annotation value ON pkg_annotation.value_id = value.annotation_id "
135  				"   WHERE tag.annotation = 'flavor') "
136  				"SELECT DISTINCT(p.id), origin, p.name, p.name as uniqueid, "
137  				"   version, comment, desc, "
138  				"   message, arch, maintainer, www, "
139  				"   prefix, flatsize, licenselogic, automatic, "
140  				"   locked, time, manifestdigest, vital "
141  				"   FROM packages AS p "
142  				"   LEFT JOIN pkg_categories ON p.id = pkg_categories.package_id "
143  				"   LEFT JOIN categories ON categories.id = pkg_categories.category_id "
144  				"   LEFT JOIN flavors ON flavors.package_id = p.id "
145  				"    %s %s (%s) ORDER BY p.name;",
146  					comp, pattern == NULL ? "WHERE" : "AND", cond + 7);
147  	} else if (match == MATCH_INTERNAL) {
148  		sqlite3_snprintf(sizeof(sql), sql,
149  				"SELECT DISTINCT(p.id), origin, p.name, p.name as uniqueid, "
150  					"version, comment, desc, "
151  					"message, arch, maintainer, www, "
152  					"prefix, flatsize, licenselogic, automatic, "
153  					"locked, time, manifestdigest, vital "
154  				"FROM packages AS p "
155  				"%s"
156  				" ORDER BY p.name", comp);
157  	} else {
158  		sqlite3_snprintf(sizeof(sql), sql,
159  				"WITH flavors AS "
160  				"  (SELECT package_id, value.annotation AS flavor FROM pkg_annotation "
161  				"   LEFT JOIN annotation tag ON pkg_annotation.tag_id = tag.annotation_id "
162  				"   LEFT JOIN annotation value ON pkg_annotation.value_id = value.annotation_id "
163  				"   WHERE tag.annotation = 'flavor') "
164  				"SELECT DISTINCT(p.id), origin, p.name, p.name as uniqueid, "
165  					"version, comment, desc, "
166  					"message, arch, maintainer, www, "
167  					"prefix, flatsize, licenselogic, automatic, "
168  					"locked, time, manifestdigest, vital "
169  				"FROM packages AS p "
170  				"LEFT JOIN pkg_categories ON p.id = pkg_categories.package_id "
171  				"LEFT JOIN categories ON categories.id = pkg_categories.category_id "
172  				"LEFT JOIN flavors ON flavors.package_id = p.id "
173  				"%s"
174  				" ORDER BY p.name", comp);
175  	}
176  
177  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
178  		return (NULL);
179  
180  	if (match != MATCH_ALL)
181  		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
182  	pkgdb_debug(4, stmt);
183  
184  	return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE));
185  }
186  
187  struct pkgdb_it *
188  pkgdb_query(struct pkgdb *db, const char *pattern, match_t match)
189  {
190  	return pkgdb_query_cond(db, NULL, pattern, match);
191  }
192  
193  bool
194  pkgdb_file_exists(struct pkgdb *db, const char *path)
195  {
196  	sqlite3_stmt	*stmt;
197  	char	sql[BUFSIZ];
198  	bool	ret = false;
199  
200  	assert(db != NULL);
201  
202  	if (path == NULL)
203  		return (false);
204  
205  	sqlite3_snprintf(sizeof(sql), sql,
206  	    "select path from files where path = ?1;");
207  
208  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
209  		return (ret);
210  
211  	sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT);
212  	pkgdb_debug(4, stmt);
213  
214  	if (sqlite3_step(stmt) != SQLITE_DONE) {
215  		ret = true;
216  	}
217  
218  	sqlite3_finalize(stmt);
219  	return (ret);
220  }
221  
222  struct pkgdb_it *
223  pkgdb_query_which(struct pkgdb *db, const char *path, bool glob)
224  {
225  	sqlite3_stmt	*stmt;
226  	char	sql[BUFSIZ];
227  
228  	assert(db != NULL);
229  
230  	if (path == NULL)
231  		return (NULL);
232  
233  	sqlite3_snprintf(sizeof(sql), sql,
234  			"SELECT p.id, p.origin, p.name, p.name as uniqueid, "
235  			"p.version, p.comment, p.desc, "
236  			"p.message, p.arch, p.maintainer, p.www, "
237  			"p.prefix, p.flatsize, p.time "
238  			"FROM packages AS p "
239  			"LEFT JOIN files AS f ON p.id = f.package_id "
240  			"WHERE f.path %s ?1 GROUP BY p.id;", glob ? "GLOB" : "=");
241  
242  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
243  		return (NULL);
244  
245  	sqlite3_bind_text(stmt, 1, path, -1, SQLITE_TRANSIENT);
246  	pkgdb_debug(4, stmt);
247  
248  	return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE));
249  }
250  
251  struct pkgdb_it *
252  pkgdb_query_shlib_require(struct pkgdb *db, const char *shlib)
253  {
254  	sqlite3_stmt	*stmt;
255  	const char	 sql[] = ""
256  		"SELECT p.id, p.origin, p.name, p.name as uniqueid, "
257  			"p.version, p.comment, p.desc, "
258  			"p.message, p.arch, p.maintainer, p.www, "
259  			"p.prefix, p.flatsize, p.time "
260  			"FROM packages AS p, pkg_shlibs_required AS ps, shlibs AS s "
261  			"WHERE p.id = ps.package_id "
262  				"AND ps.shlib_id = s.id "
263  				"AND s.name = ?1;";
264  
265  	assert(db != NULL);
266  
267  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
268  		return (NULL);
269  
270  	sqlite3_bind_text(stmt, 1, shlib, -1, SQLITE_TRANSIENT);
271  	pkgdb_debug(4, stmt);
272  
273  	return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE));
274  }
275  
276  struct pkgdb_it *
277  pkgdb_query_shlib_provide(struct pkgdb *db, const char *shlib)
278  {
279  	sqlite3_stmt	*stmt;
280  	const char	 sql[] = ""
281  		"SELECT p.id, p.origin, p.name, p.name as uniqueid, "
282  			"p.version, p.comment, p.desc, "
283  			"p.message, p.arch, p.maintainer, p.www, "
284  			"p.prefix, p.flatsize, p.manifestdigest, p.time "
285  			"FROM packages AS p, pkg_shlibs_provided AS ps, shlibs AS s "
286  			"WHERE p.id = ps.package_id "
287  				"AND ps.shlib_id = s.id "
288  				"AND s.name = ?1;";
289  
290  	assert(db != NULL);
291  
292  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
293  		return (NULL);
294  
295  	sqlite3_bind_text(stmt, 1, shlib, -1, SQLITE_TRANSIENT);
296  	pkgdb_debug(4, stmt);
297  
298  	return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE));
299  }
300  
301  struct pkgdb_it *
302  pkgdb_query_require(struct pkgdb *db, const char *req)
303  {
304  	sqlite3_stmt	*stmt;
305  	const char	 sql[] = ""
306  		"SELECT p.id, p.origin, p.name, p.name as uniqueid, "
307  			"p.version, p.comment, p.desc, "
308  			"p.message, p.arch, p.maintainer, p.www, "
309  			"p.prefix, p.flatsize, p.time "
310  			"FROM packages AS p, pkg_requires AS ps, requires AS s "
311  			"WHERE p.id = ps.package_id "
312  				"AND ps.require_id = s.id "
313  				"AND s.require = ?1;";
314  
315  	assert(db != NULL);
316  
317  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
318  		return (NULL);
319  
320  	sqlite3_bind_text(stmt, 1, req, -1, SQLITE_TRANSIENT);
321  	pkgdb_debug(4, stmt);
322  
323  	return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE));
324  }
325  
326  struct pkgdb_it *
327  pkgdb_query_provide(struct pkgdb *db, const char *req)
328  {
329  	sqlite3_stmt	*stmt;
330  	const char	 sql[] = ""
331  		"SELECT p.id, p.origin, p.name, p.name as uniqueid, "
332  			"p.version, p.comment, p.desc, "
333  			"p.message, p.arch, p.maintainer, p.www, "
334  			"p.prefix, p.flatsize, p.time "
335  			"FROM packages AS p, pkg_provides AS ps, provides AS s "
336  			"WHERE p.id = ps.package_id "
337  				"AND ps.provide_id = s.id "
338  				"AND s.provide = ?1;";
339  
340  	assert(db != NULL);
341  
342  	if ((stmt = prepare_sql(db->sqlite, sql)) == NULL)
343  		return (NULL);
344  
345  	sqlite3_bind_text(stmt, 1, req, -1, SQLITE_TRANSIENT);
346  	pkgdb_debug(4, stmt);
347  
348  	return (pkgdb_it_new_sqlite(db, stmt, PKG_INSTALLED, PKGDB_IT_FLAG_ONCE));
349  }
350  
351  static bool
352  consider_this_repo(c_charv_t *repos, const char *name)
353  {
354  	/* All repositories */
355  	if (repos == NULL)
356  		return (true);
357  
358  	if (repos->len == 0)
359  		return (true);
360  
361  	return (c_charv_contains(repos, name, true));
362  }
363  
364  struct pkgdb_it *
365  pkgdb_repo_query_cond(struct pkgdb *db, const char *cond, const char *pattern, match_t match,
366      const char *repo)
367  {
368  	c_charv_t r = vec_init();
369  	struct pkgdb_it *ret;
370  
371  	if (repo != NULL)
372  		vec_push(&r, repo);
373  
374  	ret = pkgdb_repo_query_cond2(db, cond, pattern, match, &r);
375  	vec_free(&r);
376  
377  	return (ret);
378  }
379  
380  struct pkgdb_it *
381  pkgdb_repo_query_cond2(struct pkgdb *db, const char *cond, const char *pattern, match_t match,
382      c_charv_t *repos)
383  {
384  	struct pkgdb_it *it;
385  	struct pkg_repo_it *rit;
386  
387  	it = pkgdb_it_new_repo(db);
388  
389  	vec_foreach(db->repos, i) {
390  		if (consider_this_repo(repos, db->repos.d[i]->name)) {
391  			if (pattern != NULL && *pattern == '@')
392  				rit = db->repos.d[i]->ops->groupquery(db->repos.d[i], pattern + 1, match);
393  			else
394  				rit = db->repos.d[i]->ops->query(db->repos.d[i], cond, pattern, match);
395  			if (rit != NULL)
396  				pkgdb_it_repo_attach(it, rit);
397  		}
398  	}
399  
400  	return (it);
401  }
402  
403  struct pkgdb_it *pkgdb_repo_query(struct pkgdb *db, const char *pattern,
404  	match_t match, const char *repo)
405  {
406  	return pkgdb_repo_query_cond(db, NULL, pattern, match, repo);
407  }
408  
409  struct pkgdb_it *pkgdb_repo_query2(struct pkgdb *db, const char *pattern,
410  	match_t match, c_charv_t *repos)
411  {
412  	return pkgdb_repo_query_cond2(db, NULL, pattern, match, repos);
413  }
414  
415  struct pkgdb_it *
416  pkgdb_repo_shlib_require(struct pkgdb *db, const char *require, c_charv_t *repos)
417  {
418  	struct pkgdb_it *it;
419  	struct pkg_repo_it *rit;
420  
421  	it = pkgdb_it_new_repo(db);
422  
423  	vec_foreach(db->repos, i) {
424  		if (consider_this_repo(repos, db->repos.d[i]->name)) {
425  			if (db->repos.d[i]->ops->shlib_required != NULL) {
426  				rit = db->repos.d[i]->ops->shlib_required(db->repos.d[i], require);
427  				if (rit != NULL)
428  					pkgdb_it_repo_attach(it, rit);
429  			}
430  		}
431  	}
432  
433  	return (it);
434  }
435  
436  struct pkgdb_it *
437  pkgdb_repo_shlib_provide(struct pkgdb *db, const char *require, c_charv_t *repos)
438  {
439  	struct pkgdb_it *it;
440  	struct pkg_repo_it *rit;
441  
442  	it = pkgdb_it_new_repo(db);
443  
444  	vec_foreach(db->repos, i) {
445  		if (consider_this_repo(repos, db->repos.d[i]->name)) {
446  			if (db->repos.d[i]->ops->shlib_required != NULL) {
447  				rit = db->repos.d[i]->ops->shlib_provided(db->repos.d[i], require);
448  				if (rit != NULL)
449  					pkgdb_it_repo_attach(it, rit);
450  			}
451  		}
452  	}
453  
454  	return (it);
455  }
456  
457  struct pkgdb_it *
458  pkgdb_repo_require(struct pkgdb *db, const char *require, c_charv_t *repo)
459  {
460  	struct pkgdb_it *it;
461  	struct pkg_repo_it *rit;
462  
463  	it = pkgdb_it_new_repo(db);
464  
465  	vec_foreach(db->repos, i) {
466  		if (consider_this_repo(repo, db->repos.d[i]->name)) {
467  			if (db->repos.d[i]->ops->required != NULL) {
468  				rit = db->repos.d[i]->ops->required(db->repos.d[i], require);
469  				if (rit != NULL)
470  					pkgdb_it_repo_attach(it, rit);
471  			}
472  		}
473  	}
474  
475  	return (it);
476  }
477  
478  struct pkgdb_it *
479  pkgdb_repo_provide(struct pkgdb *db, const char *require, c_charv_t *repo)
480  {
481  	struct pkgdb_it *it;
482  	struct pkg_repo_it *rit;
483  
484  	it = pkgdb_it_new_repo(db);
485  
486  	vec_foreach(db->repos, i) {
487  		if (consider_this_repo(repo, db->repos.d[i]->name)) {
488  			if (db->repos.d[i]->ops->required != NULL) {
489  				rit = db->repos.d[i]->ops->provided(db->repos.d[i], require);
490  				if (rit != NULL)
491  					pkgdb_it_repo_attach(it, rit);
492  			}
493  		}
494  	}
495  
496  	return (it);
497  }
498  
499  struct pkgdb_it *
500  pkgdb_repo_search(struct pkgdb *db, const char *pattern, match_t match,
501      pkgdb_field field, pkgdb_field sort, const char *repo)
502  {
503  	c_charv_t r = vec_init();
504  	struct pkgdb_it *ret;
505  
506  	if (repo != NULL)
507  		vec_push(&r, repo);
508  
509  	ret = pkgdb_repo_search2(db, pattern, match, field, sort, &r);
510  	vec_free(&r);
511  
512  	return (ret);
513  }
514  
515  struct pkgdb_it *
516  pkgdb_repo_search2(struct pkgdb *db, const char *pattern, match_t match,
517      pkgdb_field field, pkgdb_field sort, c_charv_t *repos)
518  {
519  	struct pkgdb_it *it;
520  	struct pkg_repo_it *rit;
521  
522  	it = pkgdb_it_new_repo(db);
523  
524  	vec_foreach(db->repos, i) {
525  		if (consider_this_repo(repos, db->repos.d[i]->name)) {
526  			if (db->repos.d[i]->ops->search != NULL) {
527  				rit = db->repos.d[i]->ops->search(db->repos.d[i], pattern, match,
528  					field, sort);
529  				if (rit != NULL)
530  					pkgdb_it_repo_attach(it, rit);
531  			}
532  			if (db->repos.d[i]->ops->groupsearch != NULL) {
533  				rit = db->repos.d[i]->ops->groupsearch(db->repos.d[i], pattern, match, field);
534  				if (rit != NULL)
535  					pkgdb_it_repo_attach(it, rit);
536  			}
537  		}
538  	}
539  
540  	return (it);
541  }
542  
543  struct pkgdb_it *
544  pkgdb_all_search(struct pkgdb *db, const char *pattern, match_t match,
545      pkgdb_field field, pkgdb_field sort, const char *repo)
546  {
547  	c_charv_t r = vec_init();
548  	struct pkgdb_it *ret;
549  
550  	if (repo != NULL)
551  		vec_push(&r, repo);
552  
553  	ret = pkgdb_all_search2(db, pattern, match, field, sort, &r);
554  
555  	vec_free(&r);
556  
557  	return (ret);
558  }
559  
560  struct pkgdb_it *
561  pkgdb_all_search2(struct pkgdb *db, const char *pattern, match_t match,
562      pkgdb_field field, pkgdb_field sort, c_charv_t *repos)
563  {
564  	struct pkgdb_it *it;
565  	struct pkg_repo_it *rit;
566  
567  
568  	it = pkgdb_query(db, pattern, match);
569  
570  	vec_foreach(db->repos, i) {
571  		if (consider_this_repo(repos, db->repos.d[i]->name)) {
572  			if (db->repos.d[i]->ops->search != NULL) {
573  				rit = db->repos.d[i]->ops->search(db->repos.d[i], pattern, match,
574  					field, sort);
575  				if (rit != NULL)
576  					pkgdb_it_repo_attach(it, rit);
577  			}
578  		}
579  	}
580  
581  	return (it);
582  }