1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2013-2015 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or(at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 /**
22 * SECTION:dnf-goal
23 * @short_description: Helper methods for dealing with hawkey packages.
24 * @include: libdnf.h
25 * @stability: Unstable
26 *
27 * These methods make it easier to get and set extra data on a package.
28 */
29
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <glib.h>
37 #include <glib/gstdio.h>
38 #include <assert.h>
39
40 #include <librepo/librepo.h>
41 #include <memory>
42
43 #include "catch-error.hpp"
44 #include "dnf-context.hpp"
45 #include "dnf-package.h"
46 #include "dnf-types.h"
47 #include "dnf-utils.h"
48 #include "hy-util.h"
49 #include "repo/solvable/Dependency.hpp"
50 #include "repo/solvable/DependencyContainer.hpp"
51 #include "utils/url-encode.hpp"
52
53 typedef struct {
54 char *checksum_str;
55 gboolean user_action;
56 gchar *filename;
57 gchar *origin;
58 gchar *package_id;
59 DnfPackageInfo info;
60 DnfStateAction action;
61 DnfRepo *repo;
62 } DnfPackagePrivate;
63
64 /**
65 * dnf_package_destroy_func:
66 **/
67 static void
dnf_package_destroy_func(void * userdata)68 dnf_package_destroy_func(void *userdata)
69 {
70 DnfPackagePrivate *priv =(DnfPackagePrivate *) userdata;
71 g_free(priv->filename);
72 g_free(priv->origin);
73 g_free(priv->package_id);
74 g_free(priv->checksum_str);
75 g_slice_free(DnfPackagePrivate, priv);
76 }
77
78 /**
79 * dnf_package_get_priv:
80 **/
81 static DnfPackagePrivate *
dnf_package_get_priv(DnfPackage * pkg)82 dnf_package_get_priv(DnfPackage *pkg)
83 {
84 DnfPackagePrivate *priv;
85
86 /* create private area */
87 priv = (DnfPackagePrivate*)g_object_get_data(G_OBJECT(pkg), "DnfPackagePrivate");
88 if (priv != NULL)
89 return priv;
90
91 priv = g_slice_new0(DnfPackagePrivate);
92 g_object_set_data_full(G_OBJECT(pkg), "DnfPackagePrivate", priv, dnf_package_destroy_func);
93 return priv;
94 }
95
96 /**
97 * dnf_package_is_local:
98 * @pkg: a #DnfPackage *instance.
99 *
100 * Returns: %TRUE if the pkg is a pkg on local or media filesystem
101 *
102 * Since: 0.38.2
103 **/
104 gboolean
dnf_package_is_local(DnfPackage * pkg)105 dnf_package_is_local(DnfPackage *pkg)
106 {
107 DnfPackagePrivate *priv;
108 priv = dnf_package_get_priv(pkg);
109
110 assert(priv->repo);
111
112 if (!dnf_repo_is_local(priv->repo))
113 return FALSE;
114
115 const gchar *url_location = dnf_package_get_baseurl(pkg);
116 return (!url_location || (url_location && g_str_has_prefix(url_location, "file:/")));
117 }
118
119 /**
120 * dnf_package_get_local_baseurl:
121 * @pkg: a #DnfPackage *instance.
122 *
123 * Returns package baseurl converted to a local filesystem path (i.e. "file://"
124 * is stripped and the URL is decoded). In case the URL is not local, returns
125 * %NULL.
126 *
127 * The returned string is newly allocated and ownership is transferred to the
128 * caller.
129 *
130 * Returns: local filesystem baseurl or %NULL
131 *
132 * Since: 0.54.0
133 **/
134 gchar *
dnf_package_get_local_baseurl(DnfPackage * pkg,GError ** error)135 dnf_package_get_local_baseurl(DnfPackage *pkg, GError **error)
136 {
137 const gchar *baseurl = dnf_package_get_baseurl(pkg);
138
139 if (!baseurl || !g_str_has_prefix(baseurl, "file://")) {
140 return nullptr;
141 }
142
143 return g_strdup(libdnf::urlDecode(baseurl + 7).c_str());
144 }
145
146 /**
147 * dnf_package_get_filename:
148 * @pkg: a #DnfPackage *instance.
149 *
150 * Gets the package filename.
151 *
152 * Returns: absolute filename, or %NULL
153 *
154 * Since: 0.1.0
155 **/
156 const gchar *
dnf_package_get_filename(DnfPackage * pkg)157 dnf_package_get_filename(DnfPackage *pkg)
158 {
159 DnfPackagePrivate *priv;
160
161 priv = dnf_package_get_priv(pkg);
162 if (!priv)
163 return NULL;
164 if (dnf_package_installed(pkg))
165 return NULL;
166 if (!priv->filename && !priv->repo)
167 return NULL;
168
169 /* default cache filename location */
170 if (!priv->filename) {
171 if (dnf_package_is_local(pkg)) {
172 const gchar *url_location = dnf_package_get_baseurl(pkg);
173 if (!url_location){
174 url_location = dnf_repo_get_location(priv->repo);
175 }
176 priv->filename = g_build_filename(url_location,
177 dnf_package_get_location(pkg),
178 NULL);
179 } else {
180 /* set the filename to cachedir for non-local repos */
181 g_autofree gchar *basename = NULL;
182 basename = g_path_get_basename(dnf_package_get_location(pkg));
183 priv->filename = g_build_filename(dnf_repo_get_packages(priv->repo),
184 basename,
185 NULL);
186 }
187 g_assert (priv->filename); /* Pacify static analysis */
188 }
189
190 /* remove file:// from filename */
191 if (g_str_has_prefix(priv->filename, "file:///")){
192 gchar *tmp = priv->filename;
193 priv->filename = g_strdup(tmp + 7);
194 g_free(tmp);
195 goto out;
196 }
197
198 /* remove file: from filename */
199 if (strlen(priv->filename) >= 7){
200 if (g_str_has_prefix(priv->filename, "file:/")){
201 if (priv->filename[6] != '/'){
202 gchar *tmp = priv->filename;
203 priv->filename = g_strdup(tmp + 5);
204 g_free(tmp);
205 }
206 }
207 }
208 out:
209 return priv->filename;
210 }
211
212 /**
213 * dnf_package_get_origin:
214 * @pkg: a #DnfPackage *instance.
215 *
216 * Gets the package origin.
217 *
218 * Returns: the package origin, or %NULL
219 *
220 * Since: 0.1.0
221 **/
222 const gchar *
dnf_package_get_origin(DnfPackage * pkg)223 dnf_package_get_origin(DnfPackage *pkg)
224 {
225 DnfPackagePrivate *priv;
226 priv = dnf_package_get_priv(pkg);
227 if (priv == NULL)
228 return NULL;
229 if (!dnf_package_installed(pkg))
230 return NULL;
231 return priv->origin;
232 }
233
234 /**
235 * dnf_package_get_pkgid:
236 * @pkg: a #DnfPackage *instance.
237 *
238 * Gets the pkgid, which is the SHA hash of the package header.
239 *
240 * Returns: pkgid string, or NULL
241 *
242 * Since: 0.1.0
243 **/
244 const gchar *
dnf_package_get_pkgid(DnfPackage * pkg)245 dnf_package_get_pkgid(DnfPackage *pkg)
246 {
247 const unsigned char *checksum;
248 DnfPackagePrivate *priv;
249 int checksum_type;
250
251 priv = dnf_package_get_priv(pkg);
252 if (priv == NULL)
253 return NULL;
254 if (priv->checksum_str != NULL)
255 goto out;
256
257 /* calculate and cache */
258 checksum = dnf_package_get_hdr_chksum(pkg, &checksum_type);
259 if (checksum == NULL)
260 goto out;
261 priv->checksum_str = hy_chksum_str(checksum, checksum_type);
262 out:
263 return priv->checksum_str;
264 }
265
266 /**
267 * dnf_package_set_pkgid:
268 * @pkg: a #DnfPackage *instance.
269 * @pkgid: pkgid, e.g. "e6e3b2b10c1ef1033769147dbd1bf851c7de7699"
270 *
271 * Sets the package pkgid, which is the SHA hash of the package header.
272 *
273 * Since: 0.1.8
274 **/
275 void
dnf_package_set_pkgid(DnfPackage * pkg,const gchar * pkgid)276 dnf_package_set_pkgid(DnfPackage *pkg, const gchar *pkgid)
277 {
278 DnfPackagePrivate *priv;
279 g_return_if_fail(pkgid != NULL);
280 priv = dnf_package_get_priv(pkg);
281 if (priv == NULL)
282 return;
283 g_free(priv->checksum_str);
284 priv->checksum_str = strdup(pkgid);
285 }
286
287 /**
288 * dnf_package_id_build:
289 **/
290 static gchar *
dnf_package_id_build(const gchar * name,const gchar * version,const gchar * arch,const gchar * data)291 dnf_package_id_build(const gchar *name,
292 const gchar *version,
293 const gchar *arch,
294 const gchar *data)
295 {
296 return g_strjoin(";", name,
297 version != NULL ? version : "",
298 arch != NULL ? arch : "",
299 data != NULL ? data : "",
300 NULL);
301 }
302
303 /**
304 * dnf_package_get_package_id:
305 * @pkg: a #DnfPackage *instance.
306 *
307 * Gets the package-id as used by PackageKit.
308 *
309 * Returns: the package_id string, or %NULL, e.g. "hal;2:0.3.4;i386;installed:fedora"
310 *
311 * Since: 0.1.0
312 **/
313 const gchar *
dnf_package_get_package_id(DnfPackage * pkg)314 dnf_package_get_package_id(DnfPackage *pkg)
315 {
316 DnfPackagePrivate *priv;
317 const gchar *reponame;
318 g_autofree gchar *reponame_tmp = NULL;
319
320 priv = dnf_package_get_priv(pkg);
321 if (priv == NULL)
322 return NULL;
323 if (priv->package_id != NULL)
324 goto out;
325
326 /* calculate and cache */
327 reponame = dnf_package_get_reponame(pkg);
328 if (g_strcmp0(reponame, HY_SYSTEM_REPO_NAME) == 0) {
329 /* origin data to munge into the package_id data field */
330 if (priv->origin != NULL) {
331 reponame_tmp = g_strdup_printf("installed:%s", priv->origin);
332 reponame = reponame_tmp;
333 } else {
334 reponame = "installed";
335 }
336 } else if (g_strcmp0(reponame, HY_CMDLINE_REPO_NAME) == 0) {
337 reponame = "local";
338 }
339 priv->package_id = dnf_package_id_build(dnf_package_get_name(pkg),
340 dnf_package_get_evr(pkg),
341 dnf_package_get_arch(pkg),
342 reponame);
343 out:
344 return priv->package_id;
345 }
346
347 /**
348 * dnf_package_get_cost:
349 * @pkg: a #DnfPackage *instance.
350 *
351 * Returns the cost of the repo that provided the package.
352 *
353 * Returns: the cost, where higher is more expensive, default 1000
354 *
355 * Since: 0.1.0
356 **/
357 guint
dnf_package_get_cost(DnfPackage * pkg)358 dnf_package_get_cost(DnfPackage *pkg)
359 {
360 DnfPackagePrivate *priv;
361 priv = dnf_package_get_priv(pkg);
362 if (priv->repo == NULL) {
363 g_warning("no repo for %s", dnf_package_get_package_id(pkg));
364 return G_MAXUINT;
365 }
366 return dnf_repo_get_cost(priv->repo);
367 }
368
369 /**
370 * dnf_package_set_filename:
371 * @pkg: a #DnfPackage *instance.
372 * @filename: absolute filename.
373 *
374 * Sets the file on disk that matches the package repo.
375 *
376 * Since: 0.1.0
377 **/
378 void
dnf_package_set_filename(DnfPackage * pkg,const gchar * filename)379 dnf_package_set_filename(DnfPackage *pkg, const gchar *filename)
380 {
381 DnfPackagePrivate *priv;
382
383 /* replace contents */
384 priv = dnf_package_get_priv(pkg);
385 if (priv == NULL)
386 return;
387 g_free(priv->filename);
388 priv->filename = g_strdup(filename);
389 }
390
391 /**
392 * dnf_package_set_origin:
393 * @pkg: a #DnfPackage *instance.
394 * @origin: origin, e.g. "fedora"
395 *
396 * Sets the package origin repo.
397 *
398 * Since: 0.1.0
399 **/
400 void
dnf_package_set_origin(DnfPackage * pkg,const gchar * origin)401 dnf_package_set_origin(DnfPackage *pkg, const gchar *origin)
402 {
403 DnfPackagePrivate *priv;
404 priv = dnf_package_get_priv(pkg);
405 if (priv == NULL)
406 return;
407 g_free(priv->origin);
408 priv->origin = g_strdup(origin);
409 }
410
411 /**
412 * dnf_package_set_repo:
413 * @pkg: a #DnfPackage *instance.
414 * @repo: a #DnfRepo.
415 *
416 * Sets the repo the package was created from.
417 *
418 * Since: 0.1.0
419 **/
420 void
dnf_package_set_repo(DnfPackage * pkg,DnfRepo * repo)421 dnf_package_set_repo(DnfPackage *pkg, DnfRepo *repo)
422 {
423 DnfPackagePrivate *priv;
424 priv = dnf_package_get_priv(pkg);
425 if (priv == NULL)
426 return;
427 priv->repo = repo;
428 }
429
430 /**
431 * dnf_package_get_repo:
432 * @pkg: a #DnfPackage *instance.
433 *
434 * Gets the repo the package was created from.
435 *
436 * Returns: a #DnfRepo or %NULL
437 *
438 * Since: 0.1.0
439 **/
440 DnfRepo *
dnf_package_get_repo(DnfPackage * pkg)441 dnf_package_get_repo(DnfPackage *pkg)
442 {
443 DnfPackagePrivate *priv;
444 priv = dnf_package_get_priv(pkg);
445 if (priv == NULL)
446 return NULL;
447 return priv->repo;
448 }
449
450 /**
451 * dnf_package_get_info:
452 * @pkg: a #DnfPackage *instance.
453 *
454 * Gets the info enum assigned to the package.
455 *
456 * Returns: #DnfPackageInfo value
457 *
458 * Since: 0.1.0
459 **/
460 DnfPackageInfo
dnf_package_get_info(DnfPackage * pkg)461 dnf_package_get_info(DnfPackage *pkg)
462 {
463 DnfPackagePrivate *priv;
464 priv = dnf_package_get_priv(pkg);
465 if (priv == NULL)
466 return DNF_PACKAGE_INFO_UNKNOWN;
467 return priv->info;
468 }
469
470 /**
471 * dnf_package_get_action:
472 * @pkg: a #DnfPackage *instance.
473 *
474 * Gets the action assigned to the package, i.e. what is going to be performed.
475 *
476 * Returns: a #DnfStateAction
477 *
478 * Since: 0.1.0
479 */
480 DnfStateAction
dnf_package_get_action(DnfPackage * pkg)481 dnf_package_get_action(DnfPackage *pkg)
482 {
483 DnfPackagePrivate *priv;
484 priv = dnf_package_get_priv(pkg);
485 if (priv == NULL)
486 return DNF_STATE_ACTION_UNKNOWN;
487 return priv->action;
488 }
489
490 /**
491 * dnf_package_set_info:
492 * @pkg: a #DnfPackage *instance.
493 * @info: the info flags.
494 *
495 * Sets the info flags for the package.
496 *
497 * Since: 0.1.0
498 **/
499 void
dnf_package_set_info(DnfPackage * pkg,DnfPackageInfo info)500 dnf_package_set_info(DnfPackage *pkg, DnfPackageInfo info)
501 {
502 DnfPackagePrivate *priv;
503 priv = dnf_package_get_priv(pkg);
504 if (priv == NULL)
505 return;
506 priv->info = info;
507 }
508
509 /**
510 * dnf_package_set_action:
511 * @pkg: a #DnfPackage *instance.
512 * @action: the #DnfStateAction for the package.
513 *
514 * Sets the action for the package, i.e. what is going to be performed.
515 *
516 * Since: 0.1.0
517 */
518 void
dnf_package_set_action(DnfPackage * pkg,DnfStateAction action)519 dnf_package_set_action(DnfPackage *pkg, DnfStateAction action)
520 {
521 DnfPackagePrivate *priv;
522 priv = dnf_package_get_priv(pkg);
523 if (priv == NULL)
524 return;
525 priv->action = action;
526 }
527
528 /**
529 * dnf_package_get_user_action:
530 * @pkg: a #DnfPackage *instance.
531 *
532 * Gets if the package was installed or removed as the user action.
533 *
534 * Returns: %TRUE if the package was explicitly requested
535 *
536 * Since: 0.1.0
537 **/
538 gboolean
dnf_package_get_user_action(DnfPackage * pkg)539 dnf_package_get_user_action(DnfPackage *pkg)
540 {
541 DnfPackagePrivate *priv;
542 priv = dnf_package_get_priv(pkg);
543 if (priv == NULL)
544 return FALSE;
545 return priv->user_action;
546 }
547
548 /**
549 * dnf_package_set_user_action:
550 * @pkg: a #DnfPackage *instance.
551 * @user_action: %TRUE if the package was explicitly requested.
552 *
553 * Sets if the package was installed or removed as the user action.
554 *
555 * Since: 0.1.0
556 **/
557 void
dnf_package_set_user_action(DnfPackage * pkg,gboolean user_action)558 dnf_package_set_user_action(DnfPackage *pkg, gboolean user_action)
559 {
560 DnfPackagePrivate *priv;
561 priv = dnf_package_get_priv(pkg);
562 if (priv == NULL)
563 return;
564 priv->user_action = user_action;
565 }
566
567 /**
568 * dnf_package_is_gui:
569 * @pkg: a #DnfPackage *instance.
570 *
571 * Returns: %TRUE if the package is a GUI package
572 *
573 * Since: 0.1.0
574 **/
575 gboolean
dnf_package_is_gui(DnfPackage * pkg)576 dnf_package_is_gui(DnfPackage *pkg)
577 {
578 gboolean ret = FALSE;
579 const gchar *tmp;
580 gint idx;
581 gint size;
582
583 /* find if the package depends on GTK or KDE */
584 std::unique_ptr<DnfReldepList> reldep_list(dnf_package_get_requires(pkg));
585 size = reldep_list->count();
586 for (idx = 0; idx < size && !ret; idx++) {
587 auto reldep = reldep_list->get(idx);
588 tmp = reldep->toString();
589 if (g_strstr_len(tmp, -1, "libgtk") != NULL ||
590 g_strstr_len(tmp, -1, "libQt5Gui.so") != NULL ||
591 g_strstr_len(tmp, -1, "libQtGui.so") != NULL ||
592 g_strstr_len(tmp, -1, "libqt-mt.so") != NULL) {
593 ret = TRUE;
594 }
595 }
596
597 return ret;
598 }
599
600 /**
601 * dnf_package_is_devel:
602 * @pkg: a #DnfPackage *instance.
603 *
604 * Returns: %TRUE if the package is a development package
605 *
606 * Since: 0.1.0
607 **/
608 gboolean
dnf_package_is_devel(DnfPackage * pkg)609 dnf_package_is_devel(DnfPackage *pkg)
610 {
611 const gchar *name;
612 name = dnf_package_get_name(pkg);
613 if (g_str_has_suffix(name, "-debuginfo"))
614 return TRUE;
615 if (g_str_has_suffix(name, "-devel"))
616 return TRUE;
617 if (g_str_has_suffix(name, "-static"))
618 return TRUE;
619 if (g_str_has_suffix(name, "-libs"))
620 return TRUE;
621 return FALSE;
622 }
623
624 /**
625 * dnf_package_is_downloaded:
626 * @pkg: a #DnfPackage *instance.
627 *
628 * Returns: %TRUE if the package is already downloaded
629 *
630 * Since: 0.1.0
631 **/
632 gboolean
dnf_package_is_downloaded(DnfPackage * pkg)633 dnf_package_is_downloaded(DnfPackage *pkg)
634 {
635 const gchar *filename;
636
637 if (dnf_package_installed(pkg))
638 return FALSE;
639 filename = dnf_package_get_filename(pkg);
640 if (filename == NULL) {
641 g_warning("Failed to get cache filename for %s",
642 dnf_package_get_name(pkg));
643 return FALSE;
644 }
645 return g_file_test(filename, G_FILE_TEST_EXISTS);
646 }
647
648 /**
649 * dnf_package_is_installonly:
650 * @pkg: a #DnfPackage *instance.
651 *
652 * Returns: %TRUE if the package can be installed more than once
653 *
654 * Since: 0.1.0
655 */
656 gboolean
dnf_package_is_installonly(DnfPackage * pkg)657 dnf_package_is_installonly(DnfPackage *pkg)
658 {
659 if (auto * pkg_name = dnf_package_get_name(pkg)) {
660 auto & mainConf = libdnf::getGlobalMainConfig();
661 for (auto & inst_only_pkg_name : mainConf.installonlypkgs().getValue()) {
662 if (inst_only_pkg_name == pkg_name) {
663 return TRUE;
664 }
665 }
666 }
667 return FALSE;
668 }
669
670 /**
671 * dnf_repo_checksum_hy_to_lr:
672 **/
673 static LrChecksumType
dnf_repo_checksum_hy_to_lr(GChecksumType checksum)674 dnf_repo_checksum_hy_to_lr(GChecksumType checksum)
675 {
676 if (checksum == G_CHECKSUM_MD5)
677 return LR_CHECKSUM_MD5;
678 if (checksum == G_CHECKSUM_SHA1)
679 return LR_CHECKSUM_SHA1;
680 if (checksum == G_CHECKSUM_SHA256)
681 return LR_CHECKSUM_SHA256;
682 if (checksum == G_CHECKSUM_SHA384)
683 return LR_CHECKSUM_SHA384;
684 return LR_CHECKSUM_SHA512;
685 }
686
687 /**
688 * dnf_package_check_filename:
689 * @pkg: a #DnfPackage *instance.
690 * @valid: Set to %TRUE if the package is valid.
691 * @error: a #GError or %NULL..
692 *
693 * Checks the package is already downloaded and valid.
694 *
695 * Returns: %TRUE if the package was checked successfully
696 *
697 * Since: 0.1.0
698 **/
699 gboolean
dnf_package_check_filename(DnfPackage * pkg,gboolean * valid,GError ** error)700 dnf_package_check_filename(DnfPackage *pkg, gboolean *valid, GError **error) try
701 {
702 LrChecksumType checksum_type_lr;
703 char *checksum_valid = NULL;
704 const gchar *path;
705 const unsigned char *checksum;
706 gboolean ret = TRUE;
707 int checksum_type_hy;
708 int fd;
709
710 /* check if the file does not exist */
711 path = dnf_package_get_filename(pkg);
712 g_debug("checking if %s already exists...", path);
713 if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
714 *valid = FALSE;
715
716 /* a missing file in a local repo is an error, unless it is remote via base:url,
717 * since we can't download it */
718 if (dnf_package_is_local(pkg)) {
719 ret = FALSE;
720 g_set_error(error,
721 DNF_ERROR,
722 DNF_ERROR_INTERNAL_ERROR,
723 "File missing in local repository %s", path);
724 }
725
726 goto out;
727 }
728
729 /* check the checksum */
730 checksum = dnf_package_get_chksum(pkg, &checksum_type_hy);
731 checksum_valid = hy_chksum_str(checksum, checksum_type_hy);
732 checksum_type_lr = dnf_repo_checksum_hy_to_lr((GChecksumType)checksum_type_hy);
733 fd = g_open(path, O_RDONLY, 0);
734 if (fd < 0) {
735 ret = FALSE;
736 g_set_error(error,
737 DNF_ERROR,
738 DNF_ERROR_INTERNAL_ERROR,
739 "Failed to open %s", path);
740 goto out;
741 }
742 ret = lr_checksum_fd_cmp(checksum_type_lr,
743 fd,
744 checksum_valid,
745 TRUE, /* use xattr value */
746 valid,
747 error);
748 if (!ret) {
749 g_close(fd, NULL);
750 goto out;
751 }
752 ret = g_close(fd, error);
753 if (!ret)
754 goto out;
755
756 /* A checksum mismatch for a package in a local repository is an
757 error. We can't repair it by downloading a corrected version,
758 so let's fail here. */
759 if (!*valid && dnf_repo_is_local (dnf_package_get_repo (pkg))) {
760 ret = FALSE;
761 g_set_error(error,
762 DNF_ERROR,
763 DNF_ERROR_INTERNAL_ERROR,
764 "Checksum mismatch in local repository %s", path);
765 goto out;
766 }
767
768 out:
769 g_free(checksum_valid);
770 return ret;
771 } CATCH_TO_GERROR(FALSE)
772
773 /**
774 * dnf_package_download:
775 * @pkg: a #DnfPackage *instance.
776 * @directory: destination directory, or %NULL for the cachedir.
777 * @state: the #DnfState.
778 * @error: a #GError or %NULL..
779 *
780 * Downloads the package.
781 *
782 * Returns: the complete filename of the downloaded file
783 *
784 * Since: 0.1.0
785 **/
786 gchar *
787 dnf_package_download(DnfPackage *pkg,
788 const gchar *directory,
789 DnfState *state,
790 GError **error) try
791 {
792 DnfRepo *repo;
793 repo = dnf_package_get_repo(pkg);
794 if (repo == NULL) {
795 g_set_error_literal(error,
796 DNF_ERROR,
797 DNF_ERROR_INTERNAL_ERROR,
798 "package repo is unset");
799 return NULL;
800 }
801 return dnf_repo_download_package(repo, pkg, directory, state, error);
CATCH_TO_GERROR(NULL)802 } CATCH_TO_GERROR(NULL)
803
804 /**
805 * dnf_package_array_download:
806 * @packages: an array of packages.
807 * @directory: destination directory, or %NULL for the cachedir.
808 * @state: the #DnfState.
809 * @error: a #GError or %NULL..
810 *
811 * Downloads an array of packages.
812 *
813 * Returns: %TRUE for success
814 *
815 * Since: 0.1.0
816 */
817 gboolean
818 dnf_package_array_download(GPtrArray *packages,
819 const gchar *directory,
820 DnfState *state,
821 GError **error) try
822 {
823 DnfState *state_local;
824 GHashTableIter hiter;
825 gpointer key, value;
826 guint i;
827 g_autoptr(GHashTable) repo_to_packages = NULL;
828
829 /* map packages to repos */
830 repo_to_packages = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_ptr_array_unref);
831 for (i = 0; i < packages->len; i++) {
832 DnfPackage *pkg = (DnfPackage*)g_ptr_array_index(packages, i);
833 DnfRepo *repo;
834 GPtrArray *repo_packages;
835
836 repo = dnf_package_get_repo(pkg);
837 if (repo == NULL) {
838 g_set_error_literal(error,
839 DNF_ERROR,
840 DNF_ERROR_INTERNAL_ERROR,
841 "package repo is unset");
842 return FALSE;
843 }
844 repo_packages = (GPtrArray*)g_hash_table_lookup(repo_to_packages, repo);
845 if (repo_packages == NULL) {
846 repo_packages = g_ptr_array_new();
847 g_hash_table_insert(repo_to_packages, repo, repo_packages);
848 }
849 g_ptr_array_add(repo_packages, pkg);
850 }
851
852 /* set steps according to the number of repos we are going to download from */
853 dnf_state_set_number_steps(state, g_hash_table_size(repo_to_packages));
854
855 /* download all packages from each repo in one go */
856 g_hash_table_iter_init(&hiter, repo_to_packages);
857 while (g_hash_table_iter_next(&hiter, &key, &value)) {
858 DnfRepo *repo = (DnfRepo*)key;
859 GPtrArray *repo_packages = (GPtrArray*)value;
860
861 state_local = dnf_state_get_child(state);
862 if (!dnf_repo_download_packages(repo, repo_packages, directory, state_local, error))
863 return FALSE;
864
865 /* done */
866 if (!dnf_state_done(state, error))
867 return FALSE;
868 }
869 return TRUE;
870 } CATCH_TO_GERROR(FALSE)
871
872 /**
873 * dnf_package_array_get_download_size:
874 * @packages: an array of packages.
875 *
876 * Gets the download size for an array of packages.
877 *
878 * Returns: the download size
879 *
880 * Since: 0.2.3
881 */
882 guint64
883 dnf_package_array_get_download_size(GPtrArray *packages)
884 {
885 guint i;
886 guint64 download_size = 0;
887
888 for (i = 0; i < packages->len; i++) {
889 DnfPackage *pkg = (DnfPackage*)g_ptr_array_index(packages, i);
890
891 download_size += dnf_package_get_downloadsize(pkg);
892 }
893
894 return download_size;
895 }
896