1 /*
2  * libosinfo: An installation tree for a (guest) OS
3  *
4  * Copyright (C) 2009-2020 Red Hat, Inc.
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, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <osinfo/osinfo.h>
22 #include "osinfo_util_private.h"
23 #include <gio/gio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib/gi18n-lib.h>
27 #include <libsoup/soup.h>
28 
29 typedef struct _CreateFromLocationAsyncData CreateFromLocationAsyncData;
30 struct _CreateFromLocationAsyncData {
31     SoupSession *session;
32     SoupMessage *message;
33     GFile *file;
34 
35     gchar *content;
36     gchar *location;
37     gchar *treeinfo;
38 
39     GTask *res;
40 };
41 
create_from_location_async_data_free(CreateFromLocationAsyncData * data)42 static void create_from_location_async_data_free(CreateFromLocationAsyncData *data)
43 {
44     g_clear_object(&data->session);
45     g_clear_object(&data->message);
46     g_clear_object(&data->file);
47     g_clear_object(&data->res);
48 
49     g_slice_free(CreateFromLocationAsyncData, data);
50 }
51 
52 typedef struct _CreateFromLocationData CreateFromLocationData;
53 struct _CreateFromLocationData {
54     GMainLoop *main_loop;
55 
56     GAsyncResult *res;
57 };
58 
create_from_location_data_free(CreateFromLocationData * data)59 static void create_from_location_data_free(CreateFromLocationData *data)
60 {
61     g_object_unref(data->res);
62     g_main_loop_unref(data->main_loop);
63 
64     g_slice_free(CreateFromLocationData, data);
65 }
66 
67 /**
68  * osinfo_tree_error_quark:
69  *
70  * Gets a #GQuark representing the string "osinfo-tree-error"
71  *
72  * Returns: the #GQuark representing the string.
73  *
74  * Since: 0.1.0
75  */
76 GQuark
osinfo_tree_error_quark(void)77 osinfo_tree_error_quark(void)
78 {
79     static GQuark quark = 0;
80 
81     if (!quark)
82         quark = g_quark_from_static_string("osinfo-tree-error");
83 
84     return quark;
85 }
86 
87 /**
88  * SECTION:osinfo_tree
89  * @short_description: An installation tree for a (guest) OS
90  * @see_also: #OsinfoOs
91  *
92  * #OsinfoTree is an entity representing an installation tree
93  * a (guest) operating system.
94  */
95 
96 struct _OsinfoTreePrivate
97 {
98     GWeakRef os;
99 };
100 
101 G_DEFINE_TYPE_WITH_PRIVATE(OsinfoTree, osinfo_tree, OSINFO_TYPE_ENTITY);
102 
103 enum {
104     PROP_0,
105 
106     PROP_ARCHITECTURE,
107     PROP_URL,
108     PROP_TREEINFO_FAMILY,
109     PROP_TREEINFO_VARIANT,
110     PROP_TREEINFO_VERSION,
111     PROP_TREEINFO_ARCH,
112     PROP_KERNEL_PATH,
113     PROP_INITRD_PATH,
114     PROP_BOOT_ISO_PATH,
115     PROP_HAS_TREEINFO,
116     PROP_OS,
117 
118     LAST_PROP
119 };
120 static GParamSpec *properties[LAST_PROP];
121 
122 static void
osinfo_tree_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)123 osinfo_tree_get_property(GObject *object,
124                          guint property_id,
125                          GValue *value,
126                          GParamSpec *pspec)
127 {
128     OsinfoTree *tree = OSINFO_TREE(object);
129 
130     switch (property_id) {
131     case PROP_ARCHITECTURE:
132         g_value_set_string(value,
133                            osinfo_tree_get_architecture(tree));
134         break;
135 
136     case PROP_URL:
137         g_value_set_string(value,
138                            osinfo_tree_get_url(tree));
139         break;
140 
141     case PROP_TREEINFO_FAMILY:
142         g_value_set_string(value,
143                            osinfo_tree_get_treeinfo_family(tree));
144         break;
145 
146     case PROP_TREEINFO_VARIANT:
147         g_value_set_string(value,
148                            osinfo_tree_get_treeinfo_variant(tree));
149         break;
150 
151     case PROP_TREEINFO_VERSION:
152         g_value_set_string(value,
153                            osinfo_tree_get_treeinfo_version(tree));
154         break;
155 
156     case PROP_TREEINFO_ARCH:
157         g_value_set_string(value,
158                            osinfo_tree_get_treeinfo_arch(tree));
159         break;
160 
161     case PROP_KERNEL_PATH:
162         g_value_set_string(value,
163                            osinfo_tree_get_kernel_path(tree));
164         break;
165 
166     case PROP_INITRD_PATH:
167         g_value_set_string(value,
168                            osinfo_tree_get_initrd_path(tree));
169         break;
170 
171     case PROP_BOOT_ISO_PATH:
172         g_value_set_string(value,
173                            osinfo_tree_get_boot_iso_path(tree));
174         break;
175 
176     case PROP_HAS_TREEINFO:
177         g_value_set_boolean(value,
178                             osinfo_tree_has_treeinfo(tree));
179         break;
180 
181     case PROP_OS:
182         g_value_take_object(value, osinfo_tree_get_os(tree));
183         break;
184 
185     default:
186         /* We don't have any other property... */
187         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
188         break;
189     }
190 }
191 
192 
193 static void
osinfo_tree_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)194 osinfo_tree_set_property(GObject      *object,
195                          guint         property_id,
196                          const GValue *value,
197                          GParamSpec   *pspec)
198 {
199     OsinfoTree *tree = OSINFO_TREE(object);
200 
201     switch (property_id) {
202     case PROP_ARCHITECTURE:
203         osinfo_entity_set_param(OSINFO_ENTITY(tree),
204                                 OSINFO_TREE_PROP_ARCHITECTURE,
205                                 g_value_get_string(value));
206         break;
207 
208     case PROP_URL:
209         osinfo_entity_set_param(OSINFO_ENTITY(tree),
210                                 OSINFO_TREE_PROP_URL,
211                                 g_value_get_string(value));
212         break;
213 
214     case PROP_TREEINFO_FAMILY:
215         osinfo_entity_set_param(OSINFO_ENTITY(tree),
216                                 OSINFO_TREE_PROP_TREEINFO_FAMILY,
217                                 g_value_get_string(value));
218         break;
219 
220     case PROP_TREEINFO_VARIANT:
221         osinfo_entity_set_param(OSINFO_ENTITY(tree),
222                                 OSINFO_TREE_PROP_TREEINFO_VARIANT,
223                                 g_value_get_string(value));
224         break;
225 
226     case PROP_TREEINFO_VERSION:
227         osinfo_entity_set_param(OSINFO_ENTITY(tree),
228                                 OSINFO_TREE_PROP_TREEINFO_VERSION,
229                                 g_value_get_string(value));
230         break;
231 
232     case PROP_TREEINFO_ARCH:
233         osinfo_entity_set_param(OSINFO_ENTITY(tree),
234                                 OSINFO_TREE_PROP_TREEINFO_ARCH,
235                                 g_value_get_string(value));
236         break;
237 
238     case PROP_KERNEL_PATH:
239         osinfo_entity_set_param(OSINFO_ENTITY(tree),
240                                 OSINFO_TREE_PROP_KERNEL,
241                                 g_value_get_string(value));
242         break;
243 
244     case PROP_INITRD_PATH:
245         osinfo_entity_set_param(OSINFO_ENTITY(tree),
246                                 OSINFO_TREE_PROP_INITRD,
247                                 g_value_get_string(value));
248         break;
249 
250     case PROP_BOOT_ISO_PATH:
251         osinfo_entity_set_param(OSINFO_ENTITY(tree),
252                                 OSINFO_TREE_PROP_BOOT_ISO,
253                                 g_value_get_string(value));
254         break;
255 
256     case PROP_HAS_TREEINFO:
257         osinfo_entity_set_param_boolean(OSINFO_ENTITY(tree),
258                                         OSINFO_TREE_PROP_HAS_TREEINFO,
259                                         g_value_get_boolean(value));
260         break;
261 
262     case PROP_OS:
263         osinfo_tree_set_os(tree, g_value_get_object(value));
264         break;
265 
266     default:
267         /* We don't have any other property... */
268         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
269         break;
270     }
271 }
272 
273 static void
osinfo_tree_finalize(GObject * object)274 osinfo_tree_finalize(GObject *object)
275 {
276     /* Chain up to the parent class */
277     G_OBJECT_CLASS(osinfo_tree_parent_class)->finalize(object);
278 }
279 
osinfo_tree_dispose(GObject * obj)280 static void osinfo_tree_dispose(GObject *obj)
281 {
282     OsinfoTree *tree = OSINFO_TREE(obj);
283 
284     g_weak_ref_clear(&tree->priv->os);
285 
286     G_OBJECT_CLASS(osinfo_tree_parent_class)->dispose(obj);
287 }
288 
289 /* Init functions */
290 static void
osinfo_tree_class_init(OsinfoTreeClass * klass)291 osinfo_tree_class_init(OsinfoTreeClass *klass)
292 {
293     GObjectClass *g_klass = G_OBJECT_CLASS(klass);
294 
295     g_klass->dispose = osinfo_tree_dispose;
296     g_klass->finalize = osinfo_tree_finalize;
297     g_klass->get_property = osinfo_tree_get_property;
298     g_klass->set_property = osinfo_tree_set_property;
299 
300     /**
301      * OsinfoTree:architecture:
302      *
303      * The target hardware architecture of this tree.
304      */
305     properties[PROP_ARCHITECTURE] = g_param_spec_string("architecture",
306                                                         "ARCHITECTURE",
307                                                         _("CPU Architecture"),
308                                                         NULL /* default value */,
309                                                         G_PARAM_READWRITE |
310                                                         G_PARAM_STATIC_STRINGS);
311 
312     /**
313      * OsinfoTree:url:
314      *
315      * The URL to this tree.
316      */
317     properties[PROP_URL] = g_param_spec_string("url",
318                                                "URL",
319                                                _("The URL to this tree"),
320                                                NULL /* default value */,
321                                                G_PARAM_READWRITE |
322                                                G_PARAM_STATIC_STRINGS);
323 
324     /**
325      * OsinfoTree:kernel-path:
326      *
327      * The path to the kernel image in the install tree.
328      */
329     properties[PROP_KERNEL_PATH] = g_param_spec_string("kernel-path",
330                                                        "KernelPath",
331                                                        _("The path to the kernel image"),
332                                                        NULL /* default value */,
333                                                        G_PARAM_READWRITE |
334                                                        G_PARAM_STATIC_STRINGS);
335 
336     /**
337      * OsinfoTree:initrd-path:
338      *
339      * The path to the initrd image in the install tree.
340      */
341     properties[PROP_INITRD_PATH] = g_param_spec_string("initrd-path",
342                                                        "InitrdPath",
343                                                        _("The path to the initrd image"),
344                                                        NULL /* default value */,
345                                                        G_PARAM_READWRITE |
346                                                        G_PARAM_STATIC_STRINGS);
347 
348     /**
349      * OsinfoTree:boot-iso-path:
350      *
351      * The path to the boot ISO in the install tree
352      */
353     properties[PROP_BOOT_ISO_PATH] = g_param_spec_string("boot-iso-path",
354                                                          "BootISOPath",
355                                                          _("The path to the bootable ISO image"),
356                                                          NULL /* default value */,
357                                                          G_PARAM_READWRITE |
358                                                          G_PARAM_STATIC_STRINGS);
359 
360     /**
361      * OsinfoTree:has-treeinfo
362      *
363      * Whether the tree has treeinfo or not
364      */
365     properties[PROP_HAS_TREEINFO] = g_param_spec_boolean("has-treeinfo",
366                                                          "HasTreeinfo",
367                                                          _("Whether the tree has treeinfo"),
368                                                          FALSE /* default value */,
369                                                          G_PARAM_READWRITE |
370                                                          G_PARAM_STATIC_STRINGS);
371 
372     /**
373      * OsinfoTree:treeinfo-family
374      *
375      * The treeinfo family
376      */
377     properties[PROP_TREEINFO_FAMILY] = g_param_spec_string("treeinfo-family",
378                                                            "TreeInfoFamily",
379                                                            _("The treeinfo family"),
380                                                            NULL /* default value */,
381                                                            G_PARAM_READWRITE |
382                                                            G_PARAM_STATIC_STRINGS);
383 
384     /**
385      * OsinfoTree:treeinfo-variant
386      *
387      * The treeinfo variant
388      */
389     properties[PROP_TREEINFO_VARIANT] = g_param_spec_string("treeinfo-variant",
390                                                             "TreeInfoVariant",
391                                                             _("The treeinfo variant"),
392                                                             NULL /* default value */,
393                                                             G_PARAM_READWRITE |
394                                                             G_PARAM_STATIC_STRINGS);
395 
396     /**
397      * OsinfoTree:treeinfo-version
398      *
399      * The treeinfo version
400      */
401     properties[PROP_TREEINFO_VERSION] = g_param_spec_string("treeinfo-version",
402                                                             "TreeInfoVersion",
403                                                             _("The treeinfo version"),
404                                                             NULL /* default value */,
405                                                             G_PARAM_READWRITE |
406                                                             G_PARAM_STATIC_STRINGS);
407 
408     /**
409      * OsinfoTree:treeinfo-arch
410      *
411      * The treeinfo arch
412      */
413     properties[PROP_TREEINFO_ARCH] = g_param_spec_string("treeinfo-arch",
414                                                          "TreeInfoArch",
415                                                          _("The treeinfo architecture"),
416                                                          NULL /* default value */,
417                                                          G_PARAM_READWRITE |
418                                                          G_PARAM_STATIC_STRINGS);
419 
420     /**
421      * OsinfoTree:os:
422      *
423      * Os information for the current tree. For tree stored in an
424      * #OsinfoDb, it will be filled when the database is loaded, otherwise
425      * the property will be filled after a successful call to
426      * osinfo_db_identify_tree().
427      */
428     properties[PROP_OS] = g_param_spec_object("os",
429                                               "Os",
430                                               _("Information about the operating system on this tree"),
431                                               OSINFO_TYPE_OS,
432                                               G_PARAM_READWRITE |
433                                               G_PARAM_STATIC_STRINGS);
434 
435     g_object_class_install_properties(g_klass, LAST_PROP, properties);
436 }
437 
438 static void
osinfo_tree_init(OsinfoTree * tree)439 osinfo_tree_init(OsinfoTree *tree)
440 {
441     tree->priv = osinfo_tree_get_instance_private(tree);
442 
443     g_weak_ref_init(&tree->priv->os, NULL);
444 }
445 
446 /**
447  * osinfo_tree_new:
448  * @id: the id of the tree to be created
449  * @architecture: the architecture of the tree to be created
450  *
451  * Create a new tree entity
452  *
453  * Returns: (transfer full): A tree entity
454  *
455  * Since: 0.1.0
456  */
osinfo_tree_new(const gchar * id,const gchar * architecture)457 OsinfoTree *osinfo_tree_new(const gchar *id,
458                             const gchar *architecture)
459 {
460     OsinfoTree *tree;
461 
462     tree = g_object_new(OSINFO_TYPE_TREE,
463                         "id", id,
464                         NULL);
465 
466     osinfo_entity_set_param(OSINFO_ENTITY(tree),
467                             OSINFO_TREE_PROP_ARCHITECTURE,
468                             architecture);
469 
470     return tree;
471 }
472 
on_tree_create_from_location_ready(GObject * source_object,GAsyncResult * res,gpointer user_data)473 static void on_tree_create_from_location_ready(GObject *source_object,
474                                                GAsyncResult *res,
475                                                gpointer user_data)
476 {
477     CreateFromLocationData *data = (CreateFromLocationData *)user_data;
478 
479     data->res = g_object_ref(res);
480 
481     g_main_loop_quit(data->main_loop);
482 }
483 
484 /**
485  * osinfo_tree_create_from_location:
486  * @location: the location of an installation tree
487  * @cancellable: (allow-none): a #GCancellable, or %NULL
488  * @error: The location where to store any error, or %NULL
489  *
490  * Creates a new #OsinfoTree for installation tree at @location. The @location
491  * could be a http:// or a https:// URI, or a local file.
492  *
493  * NOTE: Currently this only works for trees with a .treeinfo file
494  *
495  * Returns: (transfer full): a new #OsinfoTree , or NULL on error
496  *
497  * Since: 0.1.0
498  */
osinfo_tree_create_from_location(const gchar * location,GCancellable * cancellable,GError ** error)499 OsinfoTree *osinfo_tree_create_from_location(const gchar *location,
500                                              GCancellable *cancellable,
501                                              GError **error)
502 {
503     CreateFromLocationData *data;
504     OsinfoTree *ret;
505 
506     data = g_slice_new0(CreateFromLocationData);
507     data->main_loop = g_main_loop_new(g_main_context_get_thread_default(),
508                                       FALSE);
509 
510     osinfo_tree_create_from_location_async(location,
511                                            G_PRIORITY_DEFAULT,
512                                            cancellable,
513                                            on_tree_create_from_location_ready,
514                                            data);
515 
516     /* Loop till we get a reply (or time out) */
517     g_main_loop_run(data->main_loop);
518 
519     ret = osinfo_tree_create_from_location_finish(data->res, error);
520     create_from_location_data_free(data);
521 
522     return ret;
523 }
524 
is_str_empty(const gchar * str)525 static gboolean is_str_empty(const gchar *str) {
526     guint8 i;
527     gboolean ret = TRUE;
528 
529     if ((str == NULL) || (*str == 0))
530         return TRUE;
531 
532     for (i = 0; i < strlen(str); i++)
533         if (!g_ascii_isspace(str[i])) {
534             ret = FALSE;
535 
536             break;
537         }
538 
539     return ret;
540 }
541 
is_unknown_group_or_key_error(const GError * error)542 static gboolean is_unknown_group_or_key_error(const GError *error)
543 {
544     return (g_error_matches(error, G_KEY_FILE_ERROR,
545                             G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
546             g_error_matches(error, G_KEY_FILE_ERROR,
547                             G_KEY_FILE_ERROR_GROUP_NOT_FOUND));
548 }
549 
load_keyinfo(const gchar * location,const gchar * content,gsize length,GError ** error)550 static OsinfoTree *load_keyinfo(const gchar *location,
551                                 const gchar *content,
552                                 gsize length,
553                                 GError **error)
554 {
555     GKeyFile *file = g_key_file_new();
556     OsinfoTree *tree = NULL;
557     gchar *family = NULL;
558     gchar *variant = NULL;
559     gchar *version = NULL;
560     gchar *arch = NULL;
561     gchar *kernel = NULL;
562     gchar *initrd = NULL;
563     gchar *bootiso = NULL;
564     gchar *group = NULL;
565 
566     if (!g_key_file_load_from_data(file, content, length,
567                                    G_KEY_FILE_NONE, error))
568         goto cleanup;
569 
570     if (!(family = g_key_file_get_string(file, "general", "family", error))) {
571         if (!is_unknown_group_or_key_error(*error))
572             goto cleanup;
573         g_clear_error(error);
574     }
575 
576     if (!(variant = g_key_file_get_string(file, "general", "variant", error))) {
577         if (!is_unknown_group_or_key_error(*error))
578             goto cleanup;
579         g_clear_error(error);
580     }
581 
582     if (!(version = g_key_file_get_string(file, "general", "version", error))) {
583         if (!is_unknown_group_or_key_error(*error))
584             goto cleanup;
585         g_clear_error(error);
586     }
587 
588     if (!(arch = g_key_file_get_string(file, "general", "arch", error))) {
589         if (!is_unknown_group_or_key_error(*error))
590             goto cleanup;
591         g_clear_error(error);
592     }
593 
594 
595     if (arch) {
596         group = g_strdup_printf("images-%s", arch);
597 
598         if (!(kernel = g_key_file_get_string(file, group, "kernel", error))) {
599             if (!is_unknown_group_or_key_error(*error))
600                 goto cleanup;
601             g_clear_error(error);
602         }
603 
604         if (!(initrd = g_key_file_get_string(file, group, "initrd", error))) {
605             if (!is_unknown_group_or_key_error(*error))
606                 goto cleanup;
607             g_clear_error(error);
608         }
609 
610         if (!(bootiso = g_key_file_get_string(file, group, "boot.iso", error))) {
611             if (!is_unknown_group_or_key_error(*error))
612                 goto cleanup;
613             g_clear_error(error);
614         }
615     }
616 
617     tree = osinfo_tree_new(location, arch ? arch : "i386");
618 
619     osinfo_entity_set_param(OSINFO_ENTITY(tree),
620                             OSINFO_TREE_PROP_URL,
621                             location);
622 
623     if (!is_str_empty(family))
624         osinfo_entity_set_param(OSINFO_ENTITY(tree),
625                                 OSINFO_TREE_PROP_TREEINFO_FAMILY,
626                                 family);
627     if (!is_str_empty(variant))
628         osinfo_entity_set_param(OSINFO_ENTITY(tree),
629                                 OSINFO_TREE_PROP_TREEINFO_VARIANT,
630                                 variant);
631     if (!is_str_empty(version))
632         osinfo_entity_set_param(OSINFO_ENTITY(tree),
633                                 OSINFO_TREE_PROP_TREEINFO_VERSION,
634                                 version);
635     if (!is_str_empty(arch))
636         osinfo_entity_set_param(OSINFO_ENTITY(tree),
637                                 OSINFO_TREE_PROP_TREEINFO_ARCH,
638                                 arch);
639     if (!is_str_empty(kernel))
640         osinfo_entity_set_param(OSINFO_ENTITY(tree),
641                                 OSINFO_TREE_PROP_KERNEL,
642                                 kernel);
643     if (!is_str_empty(initrd))
644         osinfo_entity_set_param(OSINFO_ENTITY(tree),
645                                 OSINFO_TREE_PROP_INITRD,
646                                 initrd);
647     if (!is_str_empty(bootiso))
648         osinfo_entity_set_param(OSINFO_ENTITY(tree),
649                                 OSINFO_TREE_PROP_BOOT_ISO,
650                                 bootiso);
651 
652  cleanup:
653     g_free(family);
654     g_free(variant);
655     g_free(version);
656     g_free(arch);
657     g_free(kernel);
658     g_free(initrd);
659     g_free(bootiso);
660     g_key_file_free(file);
661     return tree;
662 }
663 
664 static void
665 osinfo_tree_create_from_location_async_helper(CreateFromLocationAsyncData *data,
666                                               const gchar *treeinfo);
667 
on_content_read(GObject * source,GAsyncResult * res,gpointer user_data)668 static void on_content_read(GObject *source,
669                             GAsyncResult *res,
670                             gpointer user_data)
671 {
672     CreateFromLocationAsyncData *data;
673     gsize length = 0;
674     GError *error = NULL;
675     OsinfoTree *ret;
676 
677     data = (CreateFromLocationAsyncData *)user_data;
678 
679     if (!g_input_stream_read_all_finish(G_INPUT_STREAM(source),
680                                         res,
681                                         &length,
682                                         &error)) {
683         g_prefix_error(&error, _("Failed to load .treeinfo|treeinfo content: "));
684         g_task_return_error(data->res, error);
685         goto cleanup;
686     }
687 
688     if (!(ret = load_keyinfo(data->location,
689                              data->content,
690                              length,
691                              &error))) {
692         g_prefix_error(&error, _("Failed to process keyinfo file: "));
693         g_task_return_error(data->res, error);
694         goto cleanup;
695     }
696 
697     g_task_return_pointer(data->res, ret, g_object_unref);
698 
699  cleanup:
700     create_from_location_async_data_free(data);
701 }
702 
on_soup_location_read(GObject * source,GAsyncResult * res,gpointer user_data)703 static void on_soup_location_read(GObject *source,
704                                   GAsyncResult *res,
705                                   gpointer user_data)
706 {
707     CreateFromLocationAsyncData *data;
708     GError *error = NULL;
709     GInputStream *stream;
710     goffset content_size;
711 
712     data = (CreateFromLocationAsyncData *)user_data;
713 
714     stream = soup_session_send_finish(SOUP_SESSION(source),
715                                       res,
716                                       &error);
717     if (stream == NULL ||
718         !SOUP_STATUS_IS_SUCCESSFUL(data->message->status_code)) {
719         /* It means no ".treeinfo" file has been found. Try again, this time
720          * looking for a "treeinfo" file. */
721         if (g_str_equal(data->treeinfo, ".treeinfo")) {
722             osinfo_tree_create_from_location_async_helper(data, "treeinfo");
723             return;
724         }
725 
726         if (error == NULL) {
727             g_set_error_literal(&error,
728                                 OSINFO_TREE_ERROR,
729                                 OSINFO_TREE_ERROR_NO_TREEINFO,
730                                 soup_status_get_phrase(data->message->status_code));
731         }
732         g_prefix_error(&error, _("Failed to load .treeinfo|treeinfo file: "));
733         g_task_return_error(data->res, error);
734         create_from_location_async_data_free(data);
735         return;
736     }
737 
738     content_size = soup_message_headers_get_content_length(data->message->response_headers);
739     data->content = g_malloc0(content_size);
740 
741     g_input_stream_read_all_async(stream,
742                                   data->content,
743                                   content_size,
744                                   g_task_get_priority(data->res),
745                                   g_task_get_cancellable(data->res),
746                                   on_content_read,
747                                   data);
748 }
749 
on_local_location_read(GObject * source,GAsyncResult * res,gpointer user_data)750 static void on_local_location_read(GObject *source,
751                                    GAsyncResult *res,
752                                    gpointer user_data)
753 {
754     CreateFromLocationAsyncData *data;
755     GError *error = NULL;
756     gchar *content = NULL;
757     gsize length = 0;
758     OsinfoTree *ret = NULL;
759 
760     data = (CreateFromLocationAsyncData *)user_data;
761 
762     if (!g_file_load_contents_finish(G_FILE(source),
763                                      res,
764                                      &content,
765                                      &length,
766                                      NULL,
767                                      &error)) {
768         if (g_str_equal(data->treeinfo, ".treeinfo")) {
769             osinfo_tree_create_from_location_async_helper(data, "treeinfo");
770             return;
771         }
772 
773         g_prefix_error(&error, _("Failed to load .treeinfo|treeinfo file: "));
774         g_task_return_error(data->res, error);
775         goto cleanup;
776     }
777 
778     if (!(ret = load_keyinfo(data->location,
779                              content,
780                              length,
781                              &error))) {
782         g_prefix_error(&error, _("Failed to process keyinfo file: "));
783         g_task_return_error(data->res, error);
784         goto cleanup;
785     }
786 
787     g_task_return_pointer(data->res, ret, g_object_unref);
788 
789  cleanup:
790     create_from_location_async_data_free(data);
791     g_free(content);
792 }
793 
794 static void
osinfo_tree_create_from_location_async_helper(CreateFromLocationAsyncData * data,const gchar * treeinfo)795 osinfo_tree_create_from_location_async_helper(CreateFromLocationAsyncData *data,
796                                               const gchar *treeinfo)
797 {
798     gchar *location;
799     gboolean requires_soup;
800 
801     g_return_if_fail(treeinfo != NULL);
802 
803     requires_soup = osinfo_util_requires_soup(data->location);
804     if (!requires_soup &&
805         !g_str_has_prefix(data->location, "file://")) {
806         GError *error = NULL;
807 
808         g_set_error_literal(&error,
809                             OSINFO_TREE_ERROR,
810                             OSINFO_TREE_ERROR_NOT_SUPPORTED_PROTOCOL,
811                             _("URL protocol is not supported"));
812 
813         g_task_return_error(data->res, error);
814         create_from_location_async_data_free(data);
815         return;
816     }
817 
818     location = g_strdup_printf("%s/%s", data->location, treeinfo);
819 
820     g_free(data->treeinfo);
821     data->treeinfo = g_strdup(treeinfo);
822 
823     if (requires_soup) {
824         if (data->session == NULL)
825             data->session = soup_session_new_with_options(
826                     SOUP_SESSION_USER_AGENT, "Wget/1.0",
827                     NULL);
828 
829         g_clear_object(&data->message);
830         data->message = soup_message_new("GET", location);
831 
832         soup_session_send_async(data->session,
833                                 data->message,
834                                 g_task_get_cancellable(data->res),
835                                 on_soup_location_read,
836                                 data);
837     } else {
838         g_clear_object(&data->file);
839         data->file = g_file_new_for_uri(location);
840 
841         g_file_load_contents_async(data->file,
842                                    g_task_get_cancellable(data->res),
843                                    on_local_location_read,
844                                    data);
845     }
846     g_free(location);
847 }
848 
849 /**
850  * osinfo_tree_create_from_location_async:
851  * @location: the location of an installation tree
852  * @priority: the I/O priority of the request
853  * @cancellable: (allow-none): a #GCancellable, or %NULL
854  * @callback: Function to call when result of this call is ready
855  * @user_data: The user data to pass to @callback, or %NULL
856  *
857  * Asynchronous variant of #osinfo_tree_create_from_location.
858  *
859  * Since: 0.1.0
860  */
osinfo_tree_create_from_location_async(const gchar * location,gint priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)861 void osinfo_tree_create_from_location_async(const gchar *location,
862                                             gint priority,
863                                             GCancellable *cancellable,
864                                             GAsyncReadyCallback callback,
865                                             gpointer user_data)
866 {
867     CreateFromLocationAsyncData *data;
868 
869     data = g_slice_new0(CreateFromLocationAsyncData);
870     data->res = g_task_new(NULL,
871                            cancellable,
872                            callback,
873                            user_data);
874     g_task_set_priority(data->res, priority);
875 
876     data->location = g_strdup(location);
877 
878     osinfo_tree_create_from_location_async_helper(data, ".treeinfo");
879 }
880 
881 
882 /**
883  * osinfo_tree_create_from_location_finish:
884  * @res: a #GAsyncResult
885  * @error: The location where to store any error, or %NULL
886  *
887  * Finishes an asynchronous tree object creation process started with
888  * #osinfo_tree_create_from_location_async.
889  *
890  * Returns: (transfer full): a new #OsinfoTree , or NULL on error
891  *
892  * Since: 0.1.0
893  */
osinfo_tree_create_from_location_finish(GAsyncResult * res,GError ** error)894 OsinfoTree *osinfo_tree_create_from_location_finish(GAsyncResult *res,
895                                                     GError **error)
896 {
897     GTask *task = G_TASK(res);
898 
899     g_return_val_if_fail(error == NULL || *error == NULL, NULL);
900 
901     return g_task_propagate_pointer(task, error);
902 }
903 
904 /**
905  * osinfo_tree_get_architecture:
906  * @tree: an #OsinfoTree instance
907  *
908  * Retrieves the target hardware architecture of the OS @tree provides.
909  *
910  * Returns: (transfer none): the hardware architecture, or NULL
911  *
912  * Since: 0.1.0
913  */
osinfo_tree_get_architecture(OsinfoTree * tree)914 const gchar *osinfo_tree_get_architecture(OsinfoTree *tree)
915 {
916     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
917                                          OSINFO_TREE_PROP_ARCHITECTURE);
918 }
919 
920 /**
921  * osinfo_tree_get_url:
922  * @tree: an #OsinfoTree instance
923  *
924  * The URL to the @tree
925  *
926  * Returns: (transfer none): the URL, or NULL
927  *
928  * Since: 0.1.0
929  */
osinfo_tree_get_url(OsinfoTree * tree)930 const gchar *osinfo_tree_get_url(OsinfoTree *tree)
931 {
932     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
933                                          OSINFO_TREE_PROP_URL);
934 }
935 
936 /**
937  * osinfo_tree_get_treeinfo_family:
938  * @tree: an #OsinfoTree instance
939  *
940  * If @tree has treeinfo, this function retrieves the expected family.
941  *
942  * Note: In practice, this will usually not be the exact copy of the family
943  * but rather a regular expression that matches it.
944  *
945  * Returns: (transfer none): the treeinfo family, or NULL
946  *
947  * Since: 0.1.0
948  */
osinfo_tree_get_treeinfo_family(OsinfoTree * tree)949 const gchar *osinfo_tree_get_treeinfo_family(OsinfoTree *tree)
950 {
951     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
952                                          OSINFO_TREE_PROP_TREEINFO_FAMILY);
953 }
954 
955 /**
956  * osinfo_tree_get_treeinfo_arch:
957  * @tree: an #OsinfoTree instance
958  *
959  * If @tree has treeinfo, this function retrieves the expected architecture.
960  *
961  * Note: In practice, this will usually not be the exact copy of the
962  * architecture but rather a regular expression that matches it.
963  *
964  * Returns: (transfer none): the treeinfo architecture, or NULL
965  *
966  * Since: 0.1.0
967  */
osinfo_tree_get_treeinfo_arch(OsinfoTree * tree)968 const gchar *osinfo_tree_get_treeinfo_arch(OsinfoTree *tree)
969 {
970     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
971                                          OSINFO_TREE_PROP_TREEINFO_ARCH);
972 }
973 
974 /**
975  * osinfo_tree_get_treeinfo_variant:
976  * @tree: an #OsinfoTree instance
977  *
978  * If @tree has treeinfo, this function retrieves the expected variant.
979  *
980  * Note: In practice, this will usually not be the exact copy of the variant
981  * but rather a regular expression that matches it.
982  *
983  * Returns: (transfer none): the treeinfo variant, or NULL
984  *
985  * Since: 0.1.0
986  */
osinfo_tree_get_treeinfo_variant(OsinfoTree * tree)987 const gchar *osinfo_tree_get_treeinfo_variant(OsinfoTree *tree)
988 {
989     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
990                                          OSINFO_TREE_PROP_TREEINFO_VARIANT);
991 }
992 
993 /**
994  * osinfo_tree_get_treeinfo_version:
995  * @tree: an #OsinfoTree instance
996  *
997  * If @tree has treeinfo, this function retrieves the expected version.
998  *
999  * Note: In practice, this will usually not be the exact copy of version but
1000  * rather a regular expression that matches it.
1001  *
1002  * Returns: (transfer none): the treeinfo version, or NULL
1003  *
1004  * Since: 0.1.0
1005  */
osinfo_tree_get_treeinfo_version(OsinfoTree * tree)1006 const gchar *osinfo_tree_get_treeinfo_version(OsinfoTree *tree)
1007 {
1008     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
1009                                          OSINFO_TREE_PROP_TREEINFO_VERSION);
1010 }
1011 
1012 /**
1013  * osinfo_tree_get_boot_iso_path:
1014  * @tree: an #OsinfoTree instance
1015  *
1016  * Retrieves the path to the boot_iso image in the install tree.
1017  *
1018  * Returns: (transfer none): the path to boot_iso image, or NULL
1019  *
1020  * Since: 0.1.0
1021  */
osinfo_tree_get_boot_iso_path(OsinfoTree * tree)1022 const gchar *osinfo_tree_get_boot_iso_path(OsinfoTree *tree)
1023 {
1024     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
1025                                          OSINFO_TREE_PROP_BOOT_ISO);
1026 }
1027 
1028 /**
1029  * osinfo_tree_get_kernel_path:
1030  * @tree: an #OsinfoTree instance
1031  *
1032  * Retrieves the path to the kernel image in the install tree.
1033  *
1034  * Note: This only applies to installer trees of 'linux' OS family.
1035  *
1036  * Returns: (transfer none): the path to kernel image, or NULL
1037  *
1038  * Since: 0.1.0
1039  */
osinfo_tree_get_kernel_path(OsinfoTree * tree)1040 const gchar *osinfo_tree_get_kernel_path(OsinfoTree *tree)
1041 {
1042     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
1043                                          OSINFO_TREE_PROP_KERNEL);
1044 }
1045 
1046 /**
1047  * osinfo_tree_get_initrd_path:
1048  * @tree: an #OsinfoTree instance
1049  *
1050  * Retrieves the path to the initrd image in the install tree.
1051  *
1052  * Note: This only applies to installer trees of 'linux' OS family.
1053  *
1054  * Returns: (transfer none): the path to initrd image, or NULL
1055  *
1056  * Since: 0.1.0
1057  */
osinfo_tree_get_initrd_path(OsinfoTree * tree)1058 const gchar *osinfo_tree_get_initrd_path(OsinfoTree *tree)
1059 {
1060     return osinfo_entity_get_param_value(OSINFO_ENTITY(tree),
1061                                          OSINFO_TREE_PROP_INITRD);
1062 }
1063 
1064 /**
1065  * osinfo_tree_has_treeinfo:
1066  * @tree: an #OsinfoTree instance
1067  *
1068  * Return whether a tree has treeinfo or not.
1069  *
1070  * Returns: TRUE if the tree has treeinfo. FALSE otherwise.
1071  *
1072  * Since: 1.3.0
1073  */
osinfo_tree_has_treeinfo(OsinfoTree * tree)1074 gboolean osinfo_tree_has_treeinfo(OsinfoTree *tree)
1075 {
1076     return osinfo_entity_get_param_value_boolean(OSINFO_ENTITY(tree),
1077                                                  OSINFO_TREE_PROP_HAS_TREEINFO);
1078 }
1079 
1080 /**
1081  * osinfo_tree_get_os:
1082  * @tree: an #OsinfoTree instance
1083  *
1084  * Returns: (transfer full): the operating system, or NULL
1085  *
1086  * Since: 1.5.0
1087  */
osinfo_tree_get_os(OsinfoTree * tree)1088 OsinfoOs *osinfo_tree_get_os(OsinfoTree *tree)
1089 {
1090     g_return_val_if_fail(OSINFO_IS_TREE(tree), NULL);
1091 
1092     return g_weak_ref_get(&tree->priv->os);
1093 }
1094 
1095 
1096 /**
1097  * osinfo_tree_set_os
1098  * @tree: an #OsinfoTree instance
1099  * @os: an #OsinfoOs instance
1100  *
1101  * Sets the #OsinfoOs associated to the #OsinfoTree instance.
1102  *
1103  * Since: 1.5.0
1104  */
osinfo_tree_set_os(OsinfoTree * tree,OsinfoOs * os)1105 void osinfo_tree_set_os(OsinfoTree *tree, OsinfoOs *os)
1106 {
1107     g_return_if_fail(OSINFO_IS_TREE(tree));
1108 
1109     g_object_ref(os);
1110     g_weak_ref_set(&tree->priv->os, os);
1111     g_object_unref(os);
1112 }
1113 
1114 /**
1115  * osinfo_tree_get_os_variants:
1116  * @tree: an #OsinfoTree instance
1117  *
1118  * Gets the variants of the associated operating system.
1119  *
1120  * Returns: (transfer full): the operating system variant, or NULL
1121  *
1122  * Since: 1.5.0
1123  */
osinfo_tree_get_os_variants(OsinfoTree * tree)1124 OsinfoOsVariantList *osinfo_tree_get_os_variants(OsinfoTree *tree)
1125 {
1126     OsinfoOs *os;
1127     OsinfoOsVariantList *os_variants;
1128     OsinfoOsVariantList *tree_variants;
1129     GList *ids, *node;
1130     OsinfoFilter *filter;
1131 
1132     g_return_val_if_fail(OSINFO_IS_TREE(tree), NULL);
1133 
1134     os = osinfo_tree_get_os(tree);
1135     if (os == NULL)
1136         return NULL;
1137 
1138     os_variants = osinfo_os_get_variant_list(os);
1139     g_object_unref(os);
1140 
1141     ids = osinfo_entity_get_param_value_list(OSINFO_ENTITY(tree),
1142                                              OSINFO_TREE_PROP_VARIANT);
1143     filter = osinfo_filter_new();
1144     tree_variants = osinfo_os_variantlist_new();
1145     for (node = ids; node != NULL; node = node->next) {
1146         osinfo_filter_clear_constraints(filter);
1147         osinfo_filter_add_constraint(filter,
1148                                      OSINFO_ENTITY_PROP_ID,
1149                                      (const char *) node->data);
1150         osinfo_list_add_filtered(OSINFO_LIST(tree_variants),
1151                                  OSINFO_LIST(os_variants),
1152                                  filter);
1153     }
1154     g_object_unref(os_variants);
1155 
1156     return tree_variants;
1157 }
1158 
1159 /**
1160  * osinfo_tree_create_from_treeinfo:
1161  * @treeinfo: a string representing the .treeinfo content
1162  * @location: the location of the original @treeinfo
1163  * @error: The location where to store any error, or %NULL
1164  *
1165  * Creates a new #OsinfoTree for installation tree represented by @treeinfo.
1166  *
1167  * Returns: (transfer full): a new #OsinfoTree, or NULL on error
1168  *
1169  * Since: 1.7.0
1170  */
osinfo_tree_create_from_treeinfo(const gchar * treeinfo,const gchar * location,GError ** error)1171 OsinfoTree *osinfo_tree_create_from_treeinfo(const gchar *treeinfo,
1172                                              const gchar *location,
1173                                              GError **error)
1174 {
1175     g_return_val_if_fail(treeinfo != NULL, NULL);
1176     g_return_val_if_fail(location != NULL, NULL);
1177 
1178     return load_keyinfo(location, treeinfo, strlen(treeinfo), error);
1179 }
1180