More validator improvements
[appstream:appstream.git] / src / as-component.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
2  *
3  * Copyright (C) 2012-2014 Matthias Klumpp <matthias@tenstral.net>
4  *
5  * Licensed under the GNU Lesser General Public License Version 3
6  *
7  * This library is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (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
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "as-component.h"
22 #include "as-component-private.h"
23
24 #include <glib.h>
25 #include <glib-object.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <libxml/tree.h>
29 #include <libxml/parser.h>
30
31 #include "as-utils.h"
32 #include "as-utils-private.h"
33
34 /**
35  * SECTION:as-component
36  * @short_description: Object representing a software component
37  * @include: appstream.h
38  *
39  * This object represents an Appstream software component which is associated
40  * to a package in the distribution's repositories.
41  * A component can be anything, ranging from an application to a font, a codec or
42  * even a non-visual software project providing libraries and python-modules for
43  * other applications to use.
44  *
45  * The type of the component is stored as #AsComponentKind and can be queried to
46  * find out which kind of component we're dealing with.
47  *
48  * See also: #AsProvidesKind, #AsDatabase
49  */
50
51 struct _AsComponentPrivate {
52         AsComponentKind kind;
53         gchar *pkgname;
54         gchar *id;
55         gchar *name;
56         gchar *name_original;
57         gchar *summary;
58         gchar *description;
59         gchar **keywords;
60         gchar *icon;
61         gchar *icon_url;
62         gchar *homepage;
63         gchar **categories;
64         gchar **mimetypes;
65         gchar *project_license;
66         gchar *project_group;
67         gchar **compulsory_for_desktops;
68         GPtrArray *screenshots; /* of AsScreenshot elements */
69         GPtrArray *provided_items; /* of utf8 */
70         GPtrArray *releases; /* of AsRelease */
71         GHashTable *urls; /* of key:utf8 */
72         int priority; /* used internally */
73 };
74
75 static gpointer as_component_parent_class = NULL;
76
77 #define AS_COMPONENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), AS_TYPE_COMPONENT, AsComponentPrivate))
78
79 enum  {
80         AS_COMPONENT_DUMMY_PROPERTY,
81         AS_COMPONENT_KIND,
82         AS_COMPONENT_PKGNAME,
83         AS_COMPONENT_ID,
84         AS_COMPONENT_NAME,
85         AS_COMPONENT_NAME_ORIGINAL,
86         AS_COMPONENT_SUMMARY,
87         AS_COMPONENT_DESCRIPTION,
88         AS_COMPONENT_KEYWORDS,
89         AS_COMPONENT_ICON,
90         AS_COMPONENT_ICON_URL,
91         AS_COMPONENT_HOMEPAGE,
92         AS_COMPONENT_CATEGORIES,
93         AS_COMPONENT_MIMETYPES,
94         AS_COMPONENT_PROJECT_LICENSE,
95         AS_COMPONENT_SCREENSHOTS
96 };
97
98 static void as_component_finalize (GObject* obj);
99 static void as_component_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec);
100 static void as_component_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec);
101
102 /**
103  * as_component_kind_get_type:
104  *
105  * Defines registered component types.
106  */
107 GType
108 as_component_kind_get_type (void)
109 {
110         static volatile gsize as_component_kind_type_id__volatile = 0;
111         if (g_once_init_enter (&as_component_kind_type_id__volatile)) {
112                 static const GEnumValue values[] = {
113                                         {AS_COMPONENT_KIND_UNKNOWN, "AS_COMPONENT_KIND_UNKNOWN", "unknown"},
114                                         {AS_COMPONENT_KIND_GENERIC, "AS_COMPONENT_KIND_GENERIC", "generic"},
115                                         {AS_COMPONENT_KIND_DESKTOP_APP, "AS_COMPONENT_KIND_DESKTOP_APP", "desktop"},
116                                         {AS_COMPONENT_KIND_FONT, "AS_COMPONENT_KIND_FONT", "font"},
117                                         {AS_COMPONENT_KIND_CODEC, "AS_COMPONENT_KIND_CODEC", "codec"},
118                                         {AS_COMPONENT_KIND_INPUTMETHOD, "AS_COMPONENT_KIND_INPUTMETHOD", "inputmethod"},
119                                         {AS_COMPONENT_KIND_LAST, "AS_COMPONENT_KIND_LAST", "last"},
120                                         {0, NULL, NULL}
121                 };
122                 GType as_component_type_type_id;
123                 as_component_type_type_id = g_enum_register_static ("AsComponentKind", values);
124                 g_once_init_leave (&as_component_kind_type_id__volatile, as_component_type_type_id);
125         }
126         return as_component_kind_type_id__volatile;
127 }
128
129 /**
130  * as_component_kind_to_string:
131  * @kind: the %AsComponentKind.
132  *
133  * Converts the enumerated value to an text representation.
134  *
135  * Returns: string version of @kind
136  **/
137 const gchar *
138 as_component_kind_to_string (AsComponentKind kind)
139 {
140         if (kind == AS_COMPONENT_KIND_GENERIC)
141                 return "generic";
142         if (kind == AS_COMPONENT_KIND_DESKTOP_APP)
143                 return "desktop";
144         if (kind == AS_COMPONENT_KIND_FONT)
145                 return "font";
146         if (kind == AS_COMPONENT_KIND_CODEC)
147                 return "codec";
148         if (kind == AS_COMPONENT_KIND_INPUTMETHOD)
149                 return "inputmethod";
150         return "unknown";
151 }
152
153 /**
154  * as_component_kind_from_string:
155  * @kind_str: the string.
156  *
157  * Converts the text representation to an enumerated value.
158  *
159  * Returns: a #AsComponentKind or %AS_COMPONENT_KIND_UNKNOWN for unknown
160  **/
161 AsComponentKind
162 as_component_kind_from_string (const gchar *kind_str)
163 {
164         if (g_strcmp0 (kind_str, "generic") == 0)
165                 return AS_COMPONENT_KIND_GENERIC;
166         if (g_strcmp0 (kind_str, "desktop") == 0)
167                 return AS_COMPONENT_KIND_DESKTOP_APP;
168         if (g_strcmp0 (kind_str, "font") == 0)
169                 return AS_COMPONENT_KIND_FONT;
170         if (g_strcmp0 (kind_str, "codec") == 0)
171                 return AS_COMPONENT_KIND_CODEC;
172         if (g_strcmp0 (kind_str, "inputmethod") == 0)
173                 return AS_COMPONENT_KIND_INPUTMETHOD;
174         return AS_COMPONENT_KIND_UNKNOWN;
175 }
176
177 /**
178  * as_component_construct:
179  *
180  * Construct an #AsComponent.
181  *
182  * Returns: (transfer full): a new #AsComponent
183  **/
184 AsComponent*
185 as_component_construct (GType object_type)
186 {
187         AsComponent * self = NULL;
188         gchar** strv;
189         self = (AsComponent*) g_object_new (object_type, NULL);
190         as_component_set_pkgname (self, "");
191         as_component_set_id (self, "");
192         as_component_set_name_original (self, "");
193         as_component_set_summary (self, "");
194         as_component_set_description (self, "");
195         as_component_set_homepage (self, "");
196         as_component_set_icon (self, "");
197         as_component_set_icon_url (self, "");
198         as_component_set_project_license (self, "");
199         as_component_set_project_group (self, "");
200         self->priv->keywords = NULL;
201
202         strv = g_new0 (gchar*, 1 + 1);
203         strv[0] = NULL;
204         self->priv->categories = strv;
205         self->priv->screenshots = g_ptr_array_new_with_free_func (g_object_unref);
206         self->priv->provided_items = g_ptr_array_new_with_free_func (g_free);
207         self->priv->releases = g_ptr_array_new_with_free_func (g_object_unref);
208         self->priv->urls = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
209
210         as_component_set_priority (self, 0);
211
212         return self;
213 }
214
215 /**
216  * as_component_new:
217  *
218  * Creates a new #AsComponent.
219  *
220  * Returns: (transfer full): a new #AsComponent
221  **/
222 AsComponent*
223 as_component_new (void)
224 {
225         return as_component_construct (AS_TYPE_COMPONENT);
226 }
227
228 /**
229  * as_component_is_valid:
230  *
231  * Check if the essential properties of this Component are
232  * populated with useful data.
233  *
234  * Returns: TRUE if the component data was validated successfully.
235  */
236 gboolean
237 as_component_is_valid (AsComponent* self)
238 {
239         gboolean ret = FALSE;
240         AsComponentKind ctype;
241
242         g_return_val_if_fail (self != NULL, FALSE);
243
244         ctype = self->priv->kind;
245         if (ctype == AS_COMPONENT_KIND_UNKNOWN)
246                 return FALSE;
247
248         if ((g_strcmp0 (self->priv->pkgname, "") != 0) &&
249                 (g_strcmp0 (self->priv->id, "") != 0) &&
250                 (g_strcmp0 (self->priv->name, "") != 0) &&
251                 (g_strcmp0 (self->priv->name_original, "") != 0)) {
252                 ret = TRUE;
253                 }
254
255 #if 0
256         if ((ret) && ctype == AS_COMPONENT_KIND_DESKTOP_APP) {
257                 ret = g_strcmp0 (self->priv->desktop_file, "") != 0;
258         }
259 #endif
260
261         return ret;
262 }
263
264 /**
265  * as_component_to_string:
266  *
267  * Returns a string identifying this component.
268  * (useful for debugging)
269  *
270  * Returns: (transfer full): A descriptive string
271  **/
272 gchar*
273 as_component_to_string (AsComponent* self)
274 {
275         gchar* res = NULL;
276         const gchar *name;
277         g_return_val_if_fail (self != NULL, NULL);
278
279         name = as_component_get_name (self);
280         switch (self->priv->kind) {
281                 case AS_COMPONENT_KIND_DESKTOP_APP:
282                 {
283                         res = g_strdup_printf ("[DesktopApp::%s]> name: %s | package: %s | summary: %s", self->priv->id, name, self->priv->pkgname, self->priv->summary);
284                         break;
285                 }
286                 default:
287                 {
288                         res = g_strdup_printf ("[Component::%s]> name: %s | package: %s | summary: %s", self->priv->id, name, self->priv->pkgname, self->priv->summary);
289                         break;
290                 }
291         }
292
293         return res;
294 }
295
296 /**
297  * as_component_add_screenshot:
298  * @self: a #AsComponent instance.
299  * @sshot: The #AsScreenshot to add
300  *
301  * Add an #AsScreenshot to this component.
302  **/
303 void
304 as_component_add_screenshot (AsComponent* self, AsScreenshot* sshot)
305 {
306         GPtrArray* sslist;
307         g_return_if_fail (self != NULL);
308         g_return_if_fail (sshot != NULL);
309
310         sslist = as_component_get_screenshots (self);
311         g_ptr_array_add (sslist, g_object_ref (sshot));
312 }
313
314 /**
315  * as_component_add_release:
316  * @self: a #AsComponent instance.
317  * @release: The #AsRelease to add
318  *
319  * Add an #AsRelease to this component.
320  **/
321 void
322 as_component_add_release (AsComponent* self, AsRelease* release)
323 {
324         GPtrArray* releases;
325         g_return_if_fail (self != NULL);
326         g_return_if_fail (release != NULL);
327
328         releases = as_component_get_releases (self);
329         g_ptr_array_add (releases, g_object_ref (release));
330 }
331
332 /**
333  * as_component_get_urls:
334  * @self: a #AsComponent instance.
335  *
336  * Gets the URLs set for the component.
337  *
338  * Returns: (transfer none): URLs
339  *
340  * Since: 0.6.2
341  **/
342 GHashTable*
343 as_component_get_urls (AsComponent *self)
344 {
345         g_return_if_fail (self != NULL);
346         return self->priv->urls;
347 }
348
349 /**
350  * as_component_get_url:
351  * @self: a #AsComponent instance.
352  * @url_kind: the URL kind, e.g. %AS_URL_KIND_HOMEPAGE.
353  *
354  * Gets a URL.
355  *
356  * Returns: string, or %NULL if unset
357  *
358  * Since: 0.6.2
359  **/
360 const gchar *
361 as_component_get_url (AsComponent *self, AsUrlKind url_kind)
362 {
363         g_return_if_fail (self != NULL);
364         return g_hash_table_lookup (self->priv->urls,
365                                     as_url_kind_to_string (url_kind));
366 }
367
368 /**
369  * as_component_add_url:
370  * @self: a #AsComponent instance.
371  * @url_kind: the URL kind, e.g. %AS_URL_KIND_HOMEPAGE
372  * @url: the full URL.
373  *
374  * Adds some URL data to the application.
375  *
376  * Since: 0.6.2
377  **/
378 void
379 as_component_add_url (AsComponent *self,
380                                           AsUrlKind url_kind,
381                                           const gchar *url)
382 {
383         g_return_if_fail (self != NULL);
384         g_hash_table_insert (self->priv->urls,
385                              g_strdup (as_url_kind_to_string (url_kind)),
386                              g_strdup (url));
387         /* work around deprecation of "homepage" property */
388         if (url_kind == AS_URL_KIND_HOMEPAGE)
389                 as_component_set_homepage (self, url);
390 }
391
392 static void
393 _as_component_serialize_image (AsImage *img, xmlNode *subnode)
394 {
395         xmlNode* n_image = NULL;
396         gchar *size;
397         g_return_if_fail (img != NULL);
398         g_return_if_fail (subnode != NULL);
399
400         n_image = xmlNewTextChild (subnode, NULL, (xmlChar*) "image", (xmlChar*) as_image_get_url (img));
401         if (as_image_get_kind (img) == AS_IMAGE_KIND_THUMBNAIL)
402                 xmlNewProp (n_image, (xmlChar*) "type", (xmlChar*) "thumbnail");
403         else
404                 xmlNewProp (n_image, (xmlChar*) "type", (xmlChar*) "source");
405
406         size = g_strdup_printf("%i", as_image_get_width (img));
407         xmlNewProp (n_image, (xmlChar*) "width", (xmlChar*) size);
408         g_free (size);
409
410         size = g_strdup_printf("%i", as_image_get_height (img));
411         xmlNewProp (n_image, (xmlChar*) "height", (xmlChar*) size);
412         g_free (size);
413
414         xmlAddChild (subnode, n_image);
415 }
416
417 /**
418  * as_component_dump_screenshot_data_xml:
419  *
420  * Internal function to create XML which gets stored in the AppStream database
421  * for screenshots
422  */
423 gchar*
424 as_component_dump_screenshot_data_xml (AsComponent* self)
425 {
426         GPtrArray* sslist;
427         xmlDoc *doc;
428         xmlNode *root;
429         guint i;
430         AsScreenshot *sshot;
431         gchar *xmlstr = NULL;
432         g_return_val_if_fail (self != NULL, NULL);
433
434         sslist = as_component_get_screenshots (self);
435         if (sslist->len == 0) {
436                 return g_strdup ("");
437         }
438
439         doc = xmlNewDoc ((xmlChar*) NULL);
440         root = xmlNewNode (NULL, (xmlChar*) "screenshots");
441         xmlDocSetRootElement (doc, root);
442
443         for (i = 0; i < sslist->len; i++) {
444                 xmlNode *subnode;
445                 const gchar *str;
446                 GPtrArray *images;
447                 sshot = (AsScreenshot*) g_ptr_array_index (sslist, i);
448
449                 subnode = xmlNewTextChild (root, NULL, (xmlChar*) "screenshot", (xmlChar*) "");
450                 if (as_screenshot_get_kind (sshot) == AS_SCREENSHOT_KIND_DEFAULT)
451                         xmlNewProp (subnode, (xmlChar*) "type", (xmlChar*) "default");
452
453                 str = as_screenshot_get_caption (sshot);
454                 if (g_strcmp0 (str, "") != 0) {
455                         xmlNode* n_caption;
456                         n_caption = xmlNewTextChild (subnode, NULL, (xmlChar*) "caption", (xmlChar*) str);
457                         xmlAddChild (subnode, n_caption);
458                 }
459
460                 images = as_screenshot_get_images (sshot);
461                 g_ptr_array_foreach (images, (GFunc) _as_component_serialize_image, subnode);
462         }
463
464         xmlDocDumpMemory (doc, (xmlChar**) (&xmlstr), NULL);
465         xmlFreeDoc (doc);
466
467         return xmlstr;
468 }
469
470 /**
471  * as_component_load_screenshots_from_internal_xml:
472  *
473  * Internal function to load the screenshot list
474  * using the database-internal XML data.
475  */
476 void
477 as_component_load_screenshots_from_internal_xml (AsComponent* self, const gchar* xmldata)
478 {
479         xmlDoc* doc = NULL;
480         xmlNode* root = NULL;
481         xmlNode *iter;
482         g_return_if_fail (self != NULL);
483         g_return_if_fail (xmldata != NULL);
484
485         if (as_str_empty (xmldata)) {
486                 return;
487         }
488
489         doc = xmlParseDoc ((xmlChar*) xmldata);
490         root = xmlDocGetRootElement (doc);
491
492         if (root == NULL) {
493                 xmlFreeDoc (doc);
494                 return;
495         }
496
497         for (iter = root->children; iter != NULL; iter = iter->next) {
498                 if (g_strcmp0 ((gchar*) iter->name, "screenshot") == 0) {
499                         AsScreenshot* sshot;
500                         gchar *typestr;
501                         xmlNode *iter2;
502
503                         sshot = as_screenshot_new ();
504                         typestr = (gchar*) xmlGetProp (iter, (xmlChar*) "type");
505                         if (g_strcmp0 (typestr, "default") == 0)
506                                 as_screenshot_set_kind (sshot, AS_SCREENSHOT_KIND_DEFAULT);
507                         else
508                                 as_screenshot_set_kind (sshot, AS_SCREENSHOT_KIND_NORMAL);
509                         g_free (typestr);
510                         for (iter2 = iter->children; iter2 != NULL; iter2 = iter2->next) {
511                                 const gchar *node_name;
512                                 gchar *content;
513
514                                 node_name = (const gchar*) iter2->name;
515                                 content = (gchar*) xmlNodeGetContent (iter2);
516                                 if (g_strcmp0 (node_name, "image") == 0) {
517                                         AsImage *img;
518                                         gchar *str;
519                                         guint64 width;
520                                         guint64 height;
521                                         gchar *imgtype;
522                                         if (content == NULL)
523                                                 continue;
524                                         img = as_image_new ();
525
526                                         str = (gchar*) xmlGetProp (iter2, (xmlChar*) "width");
527                                         if (str == NULL) {
528                                                 g_object_unref (img);
529                                                 continue;
530                                         }
531                                         width = g_ascii_strtoll (str, NULL, 10);
532                                         g_free (str);
533
534                                         str = (gchar*) xmlGetProp (iter2, (xmlChar*) "height");
535                                         if (str == NULL) {
536                                                 g_object_unref (img);
537                                                 continue;
538                                         }
539                                         height = g_ascii_strtoll (str, NULL, 10);
540                                         g_free (str);
541
542                                         as_image_set_width (img, width);
543                                         as_image_set_height (img, height);
544
545                                         /* discard invalid elements */
546                                         if ((width == 0) || (height == 0)) {
547                                                 g_object_unref (img);
548                                                 continue;
549                                         }
550                                         as_image_set_url (img, content);
551
552                                         imgtype = (gchar*) xmlGetProp (iter2, (xmlChar*) "type");
553                                         if (g_strcmp0 (imgtype, "thumbnail") == 0) {
554                                                 as_image_set_kind (img, AS_IMAGE_KIND_THUMBNAIL);
555                                         } else {
556                                                 as_image_set_kind (img, AS_IMAGE_KIND_SOURCE);
557                                         }
558                                         g_free (imgtype);
559
560                                         as_screenshot_add_image (sshot, img);
561                                 } else if (g_strcmp0 (node_name, "caption") == 0) {
562                                         if (content != NULL)
563                                                 as_screenshot_set_caption (sshot, content);
564                                 }
565                                 g_free (content);
566                         }
567                         as_component_add_screenshot (self, sshot);
568                 }
569         }
570 }
571
572 /**
573  * as_component_complete:
574  *
575  * Private function to complete a AsComponent with
576  * additional data found on the system.
577  *
578  * @scr_base_url Base url for screenshot-service, obtain via #AsDistroDetails
579  */
580 void
581 as_component_complete (AsComponent* self, gchar *scr_base_url)
582 {
583         AsComponentPrivate *priv = self->priv;
584
585         /* we want screenshot data from 3rd-party screenshot servers, if the component doesn't have screenshots defined already */
586         if (priv->screenshots->len == 0) {
587                 gchar *url;
588                 AsImage *img;
589                 AsScreenshot *sshot;
590
591                 url = g_build_filename (scr_base_url, "screenshot", priv->pkgname, NULL);
592
593                 /* screenshots.debian.net-like services dont specify a size, so we choose the default sizes
594                  * (800x600 for source-type images, 160x120 for thumbnails)
595                  */
596
597                 /* add main image */
598                 img = as_image_new ();
599                 as_image_set_url (img, url);
600                 as_image_set_width (img, 800);
601                 as_image_set_height (img, 600);
602                 as_image_set_kind (img, AS_IMAGE_KIND_SOURCE);
603
604                 sshot = as_screenshot_new ();
605                 as_screenshot_add_image (sshot, img);
606                 as_screenshot_set_kind (sshot, AS_SCREENSHOT_KIND_DEFAULT);
607
608                 g_object_unref (img);
609                 g_free (url);
610
611                 /* add thumbnail */
612                 url = g_build_filename (scr_base_url, "thumbnail", priv->pkgname, NULL);
613                 img = as_image_new ();
614                 as_image_set_url (img, url);
615                 as_image_set_width (img, 160);
616                 as_image_set_height (img, 120);
617                 as_image_set_kind (img, AS_IMAGE_KIND_THUMBNAIL);
618                 as_screenshot_add_image (sshot, img);
619
620                 /* add screenshot to component */
621                 as_component_add_screenshot (self, sshot);
622
623                 g_object_unref (img);
624                 g_object_unref (sshot);
625                 g_free (url);
626         }
627 }
628
629 /**
630  * as_component_dump_releases_data_xml:
631  *
632  * Internal function to create XML which gets stored in the AppStream database
633  * for releases
634  */
635 gchar*
636 as_component_dump_releases_data_xml (AsComponent* self)
637 {
638         GPtrArray* releases;
639         xmlDoc *doc;
640         xmlNode *root;
641         guint i;
642         AsRelease *release;
643         gchar *xmlstr = NULL;
644         g_return_val_if_fail (self != NULL, NULL);
645
646         releases = as_component_get_releases (self);
647         if (releases->len == 0) {
648                 return g_strdup ("");
649         }
650
651         doc = xmlNewDoc ((xmlChar*) NULL);
652         root = xmlNewNode (NULL, (xmlChar*) "releases");
653         xmlDocSetRootElement (doc, root);
654
655         for (i = 0; i < releases->len; i++) {
656                 xmlNode *subnode;
657                 const gchar *str;
658                 gchar *timestamp;
659                 release = (AsRelease*) g_ptr_array_index (releases, i);
660
661                 subnode = xmlNewTextChild (root, NULL, (xmlChar*) "release", (xmlChar*) "");
662                 xmlNewProp (subnode, (xmlChar*) "version",
663                                         (xmlChar*) as_release_get_version (release));
664                 timestamp = g_strdup_printf ("%ld", as_release_get_timestamp (release));
665                 xmlNewProp (subnode, (xmlChar*) "timestamp",
666                                         (xmlChar*) timestamp);
667                 g_free (timestamp);
668
669                 str = as_release_get_description (release);
670                 if (g_strcmp0 (str, "") != 0) {
671                         xmlNode* n_desc;
672                         n_desc = xmlNewTextChild (subnode, NULL, (xmlChar*) "description", (xmlChar*) str);
673                         xmlAddChild (subnode, n_desc);
674                 }
675         }
676
677         xmlDocDumpMemory (doc, (xmlChar**) (&xmlstr), NULL);
678         xmlFreeDoc (doc);
679
680         return xmlstr;
681 }
682
683 /**
684  * as_component_load_releases_from_internal_xml:
685  *
686  * Internal function to load the releases list
687  * using the database-internal XML data.
688  */
689 void
690 as_component_load_releases_from_internal_xml (AsComponent* self, const gchar* xmldata)
691 {
692         xmlDoc* doc = NULL;
693         xmlNode* root = NULL;
694         xmlNode *iter;
695         g_return_if_fail (self != NULL);
696         g_return_if_fail (xmldata != NULL);
697
698         if (as_str_empty (xmldata)) {
699                 return;
700         }
701
702         doc = xmlParseDoc ((xmlChar*) xmldata);
703         root = xmlDocGetRootElement (doc);
704
705         if (root == NULL) {
706                 xmlFreeDoc (doc);
707                 return;
708         }
709
710         for (iter = root->children; iter != NULL; iter = iter->next) {
711                 if (g_strcmp0 ((gchar*) iter->name, "release") == 0) {
712                         AsRelease *release;
713                         gchar *prop;
714                         guint64 timestamp;
715                         xmlNode *iter2;
716                         release = as_release_new ();
717
718                         prop = (gchar*) xmlGetProp (iter, (xmlChar*) "version");
719                         as_release_set_version (release, prop);
720                         g_free (prop);
721
722                         prop = (gchar*) xmlGetProp (iter, (xmlChar*) "timestamp");
723                         timestamp = g_ascii_strtoll (prop, NULL, 10);
724                         as_release_set_timestamp (release, timestamp);
725                         g_free (prop);
726
727                         for (iter2 = iter->children; iter2 != NULL; iter2 = iter2->next) {
728                                 if (iter->type != XML_ELEMENT_NODE)
729                                         continue;
730
731                                 if (g_strcmp0 ((gchar*) iter->name, "description") == 0) {
732                                         gchar *content;
733                                         content = (gchar*) xmlNodeGetContent (iter2);
734                                         as_release_set_description (release, content);
735                                         g_free (content);
736                                         break;
737                                 }
738                         }
739
740                         as_component_add_release (self, release);
741                         g_object_unref (release);
742                 }
743         }
744 }
745
746 /**
747  * as_component_provides_item:
748  *
749  * Checks if this component provides an item of the specified type
750  *
751  * @self a valid #AsComponent
752  * @kind the kind of the provides-item
753  * @value the value of the provides-item
754  *
755  * Returns: %TRUE if an item was found
756  */
757 gboolean
758 as_component_provides_item (AsComponent *self, AsProvidesKind kind, const gchar *value)
759 {
760         guint i;
761         gboolean ret = FALSE;
762         gchar *item;
763         AsComponentPrivate *priv = self->priv;
764
765         item = as_provides_item_create (kind, value, "");
766         for (i = 0; i < priv->provided_items->len; i++) {
767                 gchar *cval;
768                 cval = (gchar*) g_ptr_array_index (priv->provided_items, i);
769                 if (g_strcmp0 (item, cval) == 0) {
770                         ret = TRUE;
771                         break;
772                 }
773         }
774
775         g_free (item);
776         return ret;
777 }
778
779 /**
780  * as_component_get_kind:
781  *
782  * Returns the #AsComponentKind of this component.
783  */
784 AsComponentKind
785 as_component_get_kind (AsComponent* self)
786 {
787         g_return_val_if_fail (self != NULL, 0);
788         return self->priv->kind;
789 }
790
791 /**
792  * as_component_set_kind:
793  *
794  * Sets the #AsComponentKind of this component.
795  */
796 void
797 as_component_set_kind (AsComponent* self, AsComponentKind value)
798 {
799         g_return_if_fail (self != NULL);
800
801         self->priv->kind = value;
802         g_object_notify ((GObject *) self, "kind");
803 }
804
805 const gchar*
806 as_component_get_pkgname (AsComponent* self)
807 {
808         g_return_val_if_fail (self != NULL, NULL);
809         return self->priv->pkgname;
810 }
811
812 void
813 as_component_set_pkgname (AsComponent* self, const gchar* value)
814 {
815         g_return_if_fail (self != NULL);
816
817         g_free (self->priv->pkgname);
818         self->priv->pkgname = g_strdup (value);
819         g_object_notify ((GObject *) self, "pkgname");
820 }
821
822 /**
823  * as_component_get_id:
824  *
825  * Set the unique identifier for this component.
826  */
827 const gchar*
828 as_component_get_id (AsComponent* self)
829 {
830         g_return_val_if_fail (self != NULL, NULL);
831         return self->priv->id;
832 }
833
834 /**
835  * as_component_set_id:
836  *
837  * Set the unique identifier for this component.
838  */
839 void
840 as_component_set_id (AsComponent* self, const gchar* value)
841 {
842         g_return_if_fail (self != NULL);
843
844         g_free (self->priv->id);
845         self->priv->id = g_strdup (value);
846         g_object_notify ((GObject *) self, "id");
847 }
848
849 const gchar*
850 as_component_get_name (AsComponent* self)
851 {
852         g_return_val_if_fail (self != NULL, NULL);
853         if (as_str_empty (self->priv->name)) {
854                 self->priv->name = g_strdup (self->priv->name_original);
855         }
856
857         return self->priv->name;
858 }
859
860 void
861 as_component_set_name (AsComponent* self, const gchar* value)
862 {
863         g_return_if_fail (self != NULL);
864
865         g_free (self->priv->name);
866         self->priv->name = g_strdup (value);
867         g_object_notify ((GObject *) self, "name");
868 }
869
870 const gchar*
871 as_component_get_name_original (AsComponent* self)
872 {
873         g_return_val_if_fail (self != NULL, NULL);
874         return self->priv->name_original;
875 }
876
877 void
878 as_component_set_name_original (AsComponent* self, const gchar* value)
879 {
880         g_return_if_fail (self != NULL);
881
882         g_free (self->priv->name_original);
883         self->priv->name_original = g_strdup (value);
884         g_object_notify ((GObject *) self, "name-original");
885 }
886
887 const gchar*
888 as_component_get_summary (AsComponent* self)
889 {
890         g_return_val_if_fail (self != NULL, NULL);
891
892         return self->priv->summary;
893 }
894
895 void
896 as_component_set_summary (AsComponent* self, const gchar* value)
897 {
898         g_return_if_fail (self != NULL);
899
900         g_free (self->priv->summary);
901         self->priv->summary = g_strdup (value);
902         g_object_notify ((GObject *) self, "summary");
903 }
904
905 const gchar*
906 as_component_get_description (AsComponent* self)
907 {
908         const gchar* result;
909         const gchar* _tmp0_ = NULL;
910         g_return_val_if_fail (self != NULL, NULL);
911         _tmp0_ = self->priv->description;
912         result = _tmp0_;
913         return result;
914 }
915
916 void
917 as_component_set_description (AsComponent* self, const gchar* value)
918 {
919         g_return_if_fail (self != NULL);
920
921         g_free (self->priv->description);
922         self->priv->description = g_strdup (value);
923         g_object_notify ((GObject *) self, "description");
924 }
925
926 /**
927  * as_component_get_keywords:
928  *
929  * Returns: (transfer full): String array of keywords
930  */
931 gchar**
932 as_component_get_keywords (AsComponent* self)
933 {
934         g_return_val_if_fail (self != NULL, NULL);
935         return self->priv->keywords;
936 }
937
938 void
939 as_component_set_keywords (AsComponent* self, gchar** value)
940 {
941         g_return_if_fail (self != NULL);
942
943         g_strfreev (self->priv->keywords);
944         self->priv->keywords = as_strv_dup (value);
945         g_object_notify ((GObject *) self, "keywords");
946 }
947
948 const gchar*
949 as_component_get_icon (AsComponent* self)
950 {
951         g_return_val_if_fail (self != NULL, NULL);
952         return self->priv->icon;
953 }
954
955 void
956 as_component_set_icon (AsComponent* self, const gchar* value)
957 {
958         g_return_if_fail (self != NULL);
959
960         g_free (self->priv->icon);
961         self->priv->icon = g_strdup (value);
962         g_object_notify ((GObject *) self, "icon");
963 }
964
965 const gchar*
966 as_component_get_icon_url (AsComponent* self)
967 {
968         g_return_val_if_fail (self != NULL, NULL);
969         return self->priv->icon_url;
970 }
971
972 void
973 as_component_set_icon_url (AsComponent* self, const gchar* value)
974 {
975         g_return_if_fail (self != NULL);
976
977         g_free (self->priv->icon_url);
978         self->priv->icon_url = g_strdup (value);
979         g_object_notify ((GObject *) self, "icon-url");
980 }
981
982 /**
983  * as_component_get_categories:
984  *
985  * Returns: (transfer none): String array of categories
986  */
987 gchar**
988 as_component_get_categories (AsComponent* self)
989 {
990         g_return_val_if_fail (self != NULL, NULL);
991
992         return self->priv->categories;
993 }
994
995 void
996 as_component_set_categories (AsComponent* self, gchar** value)
997 {
998         g_return_if_fail (self != NULL);
999
1000         g_strfreev (self->priv->categories);
1001         self->priv->categories = as_strv_dup (value);
1002         g_object_notify ((GObject *) self, "categories");
1003 }
1004
1005 /**
1006  * as_component_set_categories_from_str:
1007  *
1008  * Set the categories list from a string
1009  *
1010  * @self a valid #AsComponent instance
1011  * @categories_str Semicolon-separated list of category-names
1012  */
1013 void
1014 as_component_set_categories_from_str (AsComponent* self, const gchar* categories_str)
1015 {
1016         gchar** cats = NULL;
1017
1018         g_return_if_fail (self != NULL);
1019         g_return_if_fail (categories_str != NULL);
1020
1021         cats = g_strsplit (categories_str, ";", 0);
1022         as_component_set_categories (self, cats);
1023         g_strfreev (cats);
1024 }
1025
1026 /**
1027  * as_component_has_category:
1028  * @self an #AsComponent object
1029  *
1030  * Check if component is in the specified category.
1031  **/
1032 gboolean
1033 as_component_has_category (AsComponent* self, const gchar* category)
1034 {
1035         gchar **categories;
1036         guint i;
1037         g_return_val_if_fail (self != NULL, FALSE);
1038
1039         categories = self->priv->categories;
1040         for (i = 0; categories[i] != NULL; i++) {
1041                 if (g_strcmp0 (categories[i], category) == 0)
1042                         return TRUE;
1043         }
1044
1045         return FALSE;
1046 }
1047
1048 /**
1049  * as_component_get_mimetypes:
1050  *
1051  * Returns: (transfer full): String array of mimetype identifiers
1052  */
1053 gchar**
1054 as_component_get_mimetypes (AsComponent* self)
1055 {
1056         g_return_val_if_fail (self != NULL, NULL);
1057
1058         return self->priv->mimetypes;
1059 }
1060
1061 void
1062 as_component_set_mimetypes (AsComponent* self, gchar** value)
1063 {
1064         g_return_if_fail (self != NULL);
1065
1066         g_strfreev (self->priv->mimetypes);
1067         self->priv->mimetypes = as_strv_dup (value);
1068         g_object_notify ((GObject *) self, "mimetypes");
1069 }
1070
1071 /**
1072  * as_component_get_project_license:
1073  *
1074  * Get the license of the project this component belongs to.
1075  */
1076 const gchar*
1077 as_component_get_project_license (AsComponent* self)
1078 {
1079         g_return_val_if_fail (self != NULL, NULL);
1080         return self->priv->project_license;
1081 }
1082
1083 /**
1084  * as_component_set_project_license:
1085  *
1086  * Set the project license.
1087  */
1088 void
1089 as_component_set_project_license (AsComponent* self, const gchar* value)
1090 {
1091         g_return_if_fail (self != NULL);
1092
1093         g_free (self->priv->project_license);
1094         self->priv->project_license = g_strdup (value);
1095         g_object_notify ((GObject *) self, "project-license");
1096 }
1097
1098 /**
1099  * as_component_get_project_group:
1100  *
1101  * Get the component's project group.
1102  */
1103 const gchar*
1104 as_component_get_project_group (AsComponent* self)
1105 {
1106         g_return_val_if_fail (self != NULL, NULL);
1107         return self->priv->project_group;
1108 }
1109
1110 /**
1111  * as_component_set_project_group:
1112  *
1113  * Set the component's project group.
1114  */
1115 void
1116 as_component_set_project_group (AsComponent* self, const gchar* value)
1117 {
1118         g_return_if_fail (self != NULL);
1119
1120         g_free (self->priv->project_group);
1121         self->priv->project_group = g_strdup (value);
1122 }
1123
1124 /**
1125  * as_component_get_screenshots:
1126  *
1127  * Get a list of associated screenshots.
1128  *
1129  * Returns: (element-type AsScreenshot) (transfer none): an array of #AsScreenshot instances
1130  */
1131 GPtrArray*
1132 as_component_get_screenshots (AsComponent* self)
1133 {
1134         g_return_val_if_fail (self != NULL, NULL);
1135
1136         return self->priv->screenshots;
1137 }
1138
1139 /**
1140  * as_component_get_compulsory_for_desktops:
1141  *
1142  * Return value: (transfer none): A list of desktops where this component is compulsory
1143  **/
1144 gchar **
1145 as_component_get_compulsory_for_desktops (AsComponent* self)
1146 {
1147         g_return_val_if_fail (self != NULL, NULL);
1148
1149         return self->priv->compulsory_for_desktops;
1150 }
1151
1152 /**
1153  * as_component_set_compulsory_for_desktops:
1154  *
1155  * Set a list of desktops where this component is compulsory.
1156  **/
1157 void
1158 as_component_set_compulsory_for_desktops (AsComponent* self, gchar** value)
1159 {
1160         g_return_if_fail (self != NULL);
1161
1162         g_strfreev (self->priv->compulsory_for_desktops);
1163         self->priv->compulsory_for_desktops = as_strv_dup (value);
1164 }
1165
1166 /**
1167  * as_component_is_compulsory_for_desktop:
1168  * @self an #AsComponent object
1169  * @desktop the desktop-id to test for
1170  *
1171  * Check if this component is compulsory for the given desktop.
1172  *
1173  * Returns: %TRUE if compulsory, %FALSE otherwise.
1174  **/
1175 gboolean
1176 as_component_is_compulsory_for_desktop (AsComponent* self, const gchar* desktop)
1177 {
1178         gchar **compulsory_for_desktops;
1179         guint i;
1180         g_return_val_if_fail (self != NULL, FALSE);
1181
1182         compulsory_for_desktops = self->priv->compulsory_for_desktops;
1183         for (i = 0; compulsory_for_desktops[i] != NULL; i++) {
1184                 if (g_strcmp0 (compulsory_for_desktops[i], desktop) == 0)
1185                         return TRUE;
1186         }
1187
1188         return FALSE;
1189 }
1190
1191 /**
1192  * as_component_get_provided_items:
1193  *
1194  * Get an array of the provides-items this component is
1195  * associated with.
1196  *
1197  * Return value: (element-type utf8) (transfer none): A list of desktops where this component is compulsory
1198  **/
1199 GPtrArray*
1200 as_component_get_provided_items (AsComponent* self)
1201 {
1202         g_return_val_if_fail (self != NULL, NULL);
1203
1204         return self->priv->provided_items;
1205 }
1206
1207 /**
1208  * as_component_get_releases:
1209  *
1210  * Get an array of the #AsRelease items this component
1211  * provides.
1212  *
1213  * Return value: (element-type AsRelease) (transfer none): A list of releases
1214  **/
1215 GPtrArray*
1216 as_component_get_releases (AsComponent* self)
1217 {
1218         g_return_val_if_fail (self != NULL, NULL);
1219
1220         return self->priv->releases;
1221 }
1222
1223 /**
1224  * as_component_get_priority:
1225  *
1226  * Returns the priority of this component.
1227  * This method is used internally.
1228  *
1229  * Since: 0.6.1
1230  */
1231 int
1232 as_component_get_priority (AsComponent* self)
1233 {
1234         g_return_val_if_fail (self != NULL, 0);
1235         return self->priv->priority;
1236 }
1237
1238 /**
1239  * as_component_set_priority:
1240  *
1241  * Sets the priority of this component.
1242  * This method is used internally.
1243  *
1244  * Since: 0.6.1
1245  */
1246 void
1247 as_component_set_priority (AsComponent* self, int priority)
1248 {
1249         g_return_if_fail (self != NULL);
1250         self->priv->priority = priority;
1251 }
1252
1253 static void
1254 as_component_class_init (AsComponentClass * klass)
1255 {
1256         as_component_parent_class = g_type_class_peek_parent (klass);
1257         g_type_class_add_private (klass, sizeof (AsComponentPrivate));
1258         G_OBJECT_CLASS (klass)->get_property = as_component_get_property;
1259         G_OBJECT_CLASS (klass)->set_property = as_component_set_property;
1260         G_OBJECT_CLASS (klass)->finalize = as_component_finalize;
1261         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_KIND, g_param_spec_enum ("kind", "kind", "kind", AS_TYPE_COMPONENT_KIND, 0, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1262         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_PKGNAME, g_param_spec_string ("pkgname", "pkgname", "pkgname", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1263         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_ID, g_param_spec_string ("id", "id", "id", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1264         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_NAME, g_param_spec_string ("name", "name", "name", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1265         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_NAME_ORIGINAL, g_param_spec_string ("name-original", "name-original", "name-original", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1266         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_SUMMARY, g_param_spec_string ("summary", "summary", "summary", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1267         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_DESCRIPTION, g_param_spec_string ("description", "description", "description", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1268         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_KEYWORDS, g_param_spec_boxed ("keywords", "keywords", "keywords", G_TYPE_STRV, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1269         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_ICON, g_param_spec_string ("icon", "icon", "icon", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1270         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_ICON_URL, g_param_spec_string ("icon-url", "icon-url", "icon-url", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1271         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_HOMEPAGE, g_param_spec_string ("homepage", "homepage", "homepage", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1272         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_CATEGORIES, g_param_spec_boxed ("categories", "categories", "categories", G_TYPE_STRV, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1273         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_MIMETYPES, g_param_spec_boxed ("mimetypes", "mimetypes", "mimetypes", G_TYPE_STRV, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1274         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_PROJECT_LICENSE, g_param_spec_string ("project-license", "project-license", "project-license", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE | G_PARAM_WRITABLE));
1275         g_object_class_install_property (G_OBJECT_CLASS (klass), AS_COMPONENT_SCREENSHOTS, g_param_spec_boxed ("screenshots", "screenshots", "screenshots", G_TYPE_PTR_ARRAY, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
1276 }
1277
1278
1279 static void
1280 as_component_instance_init (AsComponent * self)
1281 {
1282         self->priv = AS_COMPONENT_GET_PRIVATE (self);
1283 }
1284
1285
1286 static void
1287 as_component_finalize (GObject* obj)
1288 {
1289         AsComponent *self;
1290         self = G_TYPE_CHECK_INSTANCE_CAST (obj, AS_TYPE_COMPONENT, AsComponent);
1291         g_free (self->priv->pkgname);
1292         g_free (self->priv->id);
1293         g_free (self->priv->name);
1294         g_free (self->priv->name_original);
1295         g_free (self->priv->summary);
1296         g_free (self->priv->description);
1297         g_free (self->priv->icon);
1298         g_free (self->priv->icon_url);
1299         g_free (self->priv->homepage);
1300         g_free (self->priv->project_license);
1301         g_free (self->priv->project_group);
1302         g_strfreev (self->priv->keywords);
1303         g_strfreev (self->priv->categories);
1304         g_strfreev (self->priv->mimetypes);
1305         g_strfreev (self->priv->compulsory_for_desktops);
1306         g_ptr_array_unref (self->priv->screenshots);
1307         g_ptr_array_unref (self->priv->provided_items);
1308         g_ptr_array_unref (self->priv->releases);
1309         g_hash_table_unref (self->priv->urls);
1310         G_OBJECT_CLASS (as_component_parent_class)->finalize (obj);
1311 }
1312
1313
1314 /**
1315  * as_component_get_type:
1316  *
1317  * Class to store data describing a component in AppStream
1318  */
1319 GType
1320 as_component_get_type (void)
1321 {
1322         static volatile gsize as_component_type_id__volatile = 0;
1323         if (g_once_init_enter (&as_component_type_id__volatile)) {
1324                 static const GTypeInfo g_define_type_info = {
1325                                         sizeof (AsComponentClass),
1326                                         (GBaseInitFunc) NULL,
1327                                         (GBaseFinalizeFunc) NULL,
1328                                         (GClassInitFunc) as_component_class_init,
1329                                         (GClassFinalizeFunc) NULL,
1330                                         NULL,
1331                                         sizeof (AsComponent),
1332                                         0,
1333                                         (GInstanceInitFunc) as_component_instance_init,
1334                                         NULL
1335                 };
1336                 GType as_component_type_id;
1337                 as_component_type_id = g_type_register_static (G_TYPE_OBJECT, "AsComponent", &g_define_type_info, 0);
1338                 g_once_init_leave (&as_component_type_id__volatile, as_component_type_id);
1339         }
1340         return as_component_type_id__volatile;
1341 }
1342
1343
1344 static void
1345 as_component_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec)
1346 {
1347         AsComponent * self;
1348         self = G_TYPE_CHECK_INSTANCE_CAST (object, AS_TYPE_COMPONENT, AsComponent);
1349         switch (property_id) {
1350                 case AS_COMPONENT_KIND:
1351                         g_value_set_enum (value, as_component_get_kind (self));
1352                         break;
1353                 case AS_COMPONENT_PKGNAME:
1354                         g_value_set_string (value, as_component_get_pkgname (self));
1355                         break;
1356                 case AS_COMPONENT_ID:
1357                         g_value_set_string (value, as_component_get_id (self));
1358                         break;
1359                 case AS_COMPONENT_NAME:
1360                         g_value_set_string (value, as_component_get_name (self));
1361                         break;
1362                 case AS_COMPONENT_NAME_ORIGINAL:
1363                         g_value_set_string (value, as_component_get_name_original (self));
1364                         break;
1365                 case AS_COMPONENT_SUMMARY:
1366                         g_value_set_string (value, as_component_get_summary (self));
1367                         break;
1368                 case AS_COMPONENT_DESCRIPTION:
1369                         g_value_set_string (value, as_component_get_description (self));
1370                         break;
1371                 case AS_COMPONENT_KEYWORDS:
1372                         g_value_set_boxed (value, as_component_get_keywords (self));
1373                         break;
1374                 case AS_COMPONENT_ICON:
1375                         g_value_set_string (value, as_component_get_icon (self));
1376                         break;
1377                 case AS_COMPONENT_ICON_URL:
1378                         g_value_set_string (value, as_component_get_icon_url (self));
1379                         break;
1380                 case AS_COMPONENT_HOMEPAGE:
1381                         g_value_set_string (value, as_component_get_homepage (self));
1382                         break;
1383                 case AS_COMPONENT_CATEGORIES:
1384                         g_value_set_boxed (value, as_component_get_categories (self));
1385                         break;
1386                 case AS_COMPONENT_MIMETYPES:
1387                         g_value_set_boxed (value, as_component_get_mimetypes (self));
1388                         break;
1389                 case AS_COMPONENT_PROJECT_LICENSE:
1390                         g_value_set_string (value, as_component_get_project_license (self));
1391                         break;
1392                 case AS_COMPONENT_SCREENSHOTS:
1393                         g_value_set_boxed (value, as_component_get_screenshots (self));
1394                         break;
1395                 default:
1396                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1397                         break;
1398         }
1399 }
1400
1401
1402 static void
1403 as_component_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec)
1404 {
1405         AsComponent * self;
1406         self = G_TYPE_CHECK_INSTANCE_CAST (object, AS_TYPE_COMPONENT, AsComponent);
1407         switch (property_id) {
1408                 case AS_COMPONENT_KIND:
1409                         as_component_set_kind (self, g_value_get_enum (value));
1410                         break;
1411                 case AS_COMPONENT_PKGNAME:
1412                         as_component_set_pkgname (self, g_value_get_string (value));
1413                         break;
1414                 case AS_COMPONENT_ID:
1415                         as_component_set_id (self, g_value_get_string (value));
1416                         break;
1417                 case AS_COMPONENT_NAME:
1418                         as_component_set_name (self, g_value_get_string (value));
1419                         break;
1420                 case AS_COMPONENT_NAME_ORIGINAL:
1421                         as_component_set_name_original (self, g_value_get_string (value));
1422                         break;
1423                 case AS_COMPONENT_SUMMARY:
1424                         as_component_set_summary (self, g_value_get_string (value));
1425                         break;
1426                 case AS_COMPONENT_DESCRIPTION:
1427                         as_component_set_description (self, g_value_get_string (value));
1428                         break;
1429                 case AS_COMPONENT_KEYWORDS:
1430                         as_component_set_keywords (self, g_value_get_boxed (value));
1431                         break;
1432                 case AS_COMPONENT_ICON:
1433                         as_component_set_icon (self, g_value_get_string (value));
1434                         break;
1435                 case AS_COMPONENT_ICON_URL:
1436                         as_component_set_icon_url (self, g_value_get_string (value));
1437                         break;
1438                 case AS_COMPONENT_HOMEPAGE:
1439                         as_component_set_homepage (self, g_value_get_string (value));
1440                         break;
1441                 case AS_COMPONENT_CATEGORIES:
1442                         as_component_set_categories (self, g_value_get_boxed (value));
1443                         break;
1444                 case AS_COMPONENT_MIMETYPES:
1445                         as_component_set_mimetypes (self, g_value_get_boxed (value));
1446                         break;
1447                 case AS_COMPONENT_PROJECT_LICENSE:
1448                         as_component_set_project_license (self, g_value_get_string (value));
1449                         break;
1450                 default:
1451                         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1452                         break;
1453         }
1454 }
1455
1456 /* --- DEPRECATED API --- */
1457
1458 /**
1459  * as_component_get_homepage:
1460  *
1461  * Deprecated: 0.6.2
1462  */
1463 const gchar*
1464 as_component_get_homepage (AsComponent* self)
1465 {
1466         g_return_val_if_fail (self != NULL, NULL);
1467
1468         return self->priv->homepage;
1469 }
1470
1471 /**
1472  * as_component_set_homepage:
1473  *
1474  * Deprecated: 0.6.2
1475  */
1476 void
1477 as_component_set_homepage (AsComponent* self, const gchar* value)
1478 {
1479         g_return_if_fail (self != NULL);
1480
1481         g_free (self->priv->homepage);
1482         self->priv->homepage = g_strdup (value);
1483         g_object_notify ((GObject *) self, "homepage");
1484 }
1485
1486 /**
1487  * as_component_get_idname:
1488  *
1489  * Deprecated: 0.6.2
1490  */
1491 const gchar*
1492 as_component_get_idname (AsComponent* self)
1493 {
1494         g_return_val_if_fail (self != NULL, NULL);
1495         return self->priv->id;
1496 }
1497
1498 /**
1499  * as_component_set_idname:
1500  *
1501  * Deprecated: 0.6.2
1502  */
1503 void
1504 as_component_set_idname (AsComponent* self, const gchar* value)
1505 {
1506         g_return_if_fail (self != NULL);
1507
1508         g_free (self->priv->id);
1509         self->priv->id = g_strdup (value);
1510         g_object_notify ((GObject *) self, "id");
1511 }