1 /* GStreamer media licenses utility functions
2 * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:gsttaglicenses
22 * @short_description: utility functions for Creative Commons licenses
23 * @see_also: #GstTagList
25 * Provides information about Creative Commons media licenses, which are
26 * often expressed in media files as a license URI in tags. Also useful
27 * for applications creating media files, in case the user wants to license
28 * the content under a Creative Commons license.
31 /* FIXME: add API to check obsolete-ness / replace-by */
43 #include "licenses-tables.dat"
45 #ifndef GST_DISABLE_GST_DEBUG
47 #define GST_CAT_DEFAULT ensure_debug_category()
49 static GstDebugCategory *
50 ensure_debug_category (void)
52 static gsize cat_gonce = 0;
54 if (g_once_init_enter (&cat_gonce)) {
57 cat_done = (gsize) _gst_debug_category_new ("tag-licenses", 0,
60 g_once_init_leave (&cat_gonce, cat_done);
63 return (GstDebugCategory *) cat_gonce;
68 #define ensure_debug_category() /* NOOP */
70 #endif /* GST_DISABLE_GST_DEBUG */
72 /* -------------------------------------------------------------------------
74 * ------------------------------------------------------------------------- */
78 gst_tag_get_license_translations_dictionary (void)
80 static gsize var_gonce = 0;
82 if (g_once_init_enter (&var_gonce)) {
83 const gchar *dict_path;
89 /* for gst-uninstalled */
90 dict_path = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_DICT");
92 if (dict_path == NULL)
93 dict_path = LICENSE_TRANSLATIONS_PATH;
95 GST_INFO ("Loading license translations from '%s'", dict_path);
96 if (g_file_get_contents (dict_path, &data, &len, &err)) {
97 var = g_variant_new_from_data (G_VARIANT_TYPE ("a{sa{ss}}"), data, len,
98 TRUE, (GDestroyNotify) g_free, data);
100 GST_WARNING ("Could not load translation dictionary %s", err->message);
102 var = g_variant_new_array (G_VARIANT_TYPE ("{sa{ss}}"), NULL, 0);
105 g_once_init_leave (&var_gonce, (gsize) var);
108 return (GVariant *) var_gonce;
114 #if !GLIB_CHECK_VERSION(2,28,0)
116 gst_g_variant_lookup_value (GVariant * dictionary, const gchar * key,
117 const GVariantType * expected_type)
123 GST_ERROR ("here, using fallback");
125 g_assert (g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{s*}")));
126 g_assert (expected_type != NULL);
128 g_variant_iter_init (&iter, dictionary);
129 while ((entry = g_variant_iter_next_value (&iter))) {
133 entry_key = g_variant_get_child_value (entry, 0);
134 matches = strcmp (g_variant_get_string (entry_key, NULL), key) == 0;
135 g_variant_unref (entry_key);
140 g_variant_unref (entry);
146 value = g_variant_get_child_value (entry, 1);
147 g_variant_unref (entry);
149 if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) {
152 tmp = g_variant_get_variant (value);
153 g_variant_unref (value);
155 if (expected_type && !g_variant_is_of_type (tmp, expected_type)) {
156 g_variant_unref (tmp);
163 g_assert (value == NULL || g_variant_is_of_type (value, expected_type));
168 #define g_variant_lookup_value gst_g_variant_lookup_value
169 #endif /* !GLIB_CHECK_VERSION(2,28,0) */
172 gst_variant_lookup_string_value (GVariant * dict, const gchar * lang,
173 const gchar ** translation)
177 trans = g_variant_lookup_value (dict, lang, G_VARIANT_TYPE ("s"));
181 *translation = g_variant_get_string (trans, NULL);
182 /* string will stay valid */
183 g_variant_unref (trans);
184 GST_TRACE ("Result: '%s' for language '%s'", *translation, lang);
190 gst_license_str_translate (const gchar * s)
193 GVariant *v, *dict, *trans;
195 v = gst_tag_get_license_translations_dictionary ();
196 g_assert (v != NULL);
198 dict = g_variant_lookup_value (v, s, G_VARIANT_TYPE ("a{ss}"));
200 const gchar *const *lang;
201 const gchar *env_lang;
204 if ((env_lang = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG"))) {
205 if (gst_variant_lookup_string_value (dict, env_lang, &s))
206 GST_TRACE ("Result: '%s' for forced language '%s'", s, env_lang);
210 lang = g_get_language_names ();
211 while (lang != NULL && *lang != NULL) {
212 GST_TRACE ("Looking up '%s' for language '%s'", s, *lang);
213 trans = g_variant_lookup_value (dict, *lang, G_VARIANT_TYPE ("s"));
216 s = g_variant_get_string (trans, NULL);
217 /* s will stay valid */
218 g_variant_unref (trans);
219 GST_TRACE ("Result: '%s'", s);
223 GST_TRACE ("No result for '%s' for language '%s'", s, *lang);
229 g_variant_unref (dict);
231 GST_WARNING ("No dict for string '%s'", s);
238 /* -------------------------------------------------------------------------
240 * ------------------------------------------------------------------------- */
242 #define CC_LICENSE_REF_PREFIX "http://creativecommons.org/licenses/"
244 /* is this license 'generic' (and a base for any of the supported
245 * jurisdictions), or jurisdiction-specific only? */
246 #define JURISDICTION_GENERIC (G_GUINT64_CONSTANT (1) << 63)
248 static const gchar jurisdictions[] =
249 "ar\000at\000au\000be\000bg\000br\000ca\000ch\000cl\000cn\000co\000de\000"
250 "dk\000es\000fi\000fr\000hr\000hu\000il\000in\000it\000jp\000kr\000mk\000"
251 "mt\000mx\000my\000nl\000pe\000pl\000pt\000scotland\000se\000si\000tw\000"
255 * gst_tag_get_licenses:
257 * Returns a list of known license references (in form of URIs). This is
258 * useful for UIs to build a list of available licenses for tagging purposes
259 * (e.g. to tag an audio track appropriately in a video or audio editor, or
260 * an image in a camera application).
262 * Returns: NULL-terminated array of license strings. Free with g_strfreev()
263 * when no longer needed.
268 gst_tag_get_licenses (void)
273 arr = g_ptr_array_new ();
274 for (i = 0; i < G_N_ELEMENTS (licenses); ++i) {
280 jbits = licenses[i].jurisdictions;
281 is_generic = (jbits & JURISDICTION_GENERIC) != 0;
283 ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, NULL);
284 GST_LOG ("Adding %2d %s (generic)", i, ref);
285 g_ptr_array_add (arr, ref);
286 jbits &= ~JURISDICTION_GENERIC;
289 jurs = jurisdictions;
292 ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, jurs, "/",
294 GST_LOG ("Adding %2d %s (%s: %s)", i, ref,
295 (is_generic) ? "derived" : "specific", jurs);
296 g_ptr_array_add (arr, ref);
298 g_assert (jurs < (jurisdictions + sizeof (jurisdictions)));
299 jurs += strlen (jurs) + 1;
303 g_ptr_array_add (arr, NULL);
304 return (gchar **) g_ptr_array_free (arr, FALSE);
308 gst_tag_get_license_idx (const gchar * license_ref, const gchar ** jurisdiction)
310 const gchar *ref, *jur_suffix;
313 GST_TRACE ("Looking up '%s'", license_ref);
315 if (!g_str_has_prefix (license_ref, CC_LICENSE_REF_PREFIX)) {
316 GST_WARNING ("unknown license prefix in ref '%s'", license_ref);
320 if (jurisdiction != NULL)
321 *jurisdiction = NULL;
323 ref = license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1;
324 for (i = 0; i < G_N_ELEMENTS (licenses); ++i) {
325 guint64 jbits = licenses[i].jurisdictions;
326 const gchar *jurs, *lref = licenses[i].ref;
327 gsize lref_len = strlen (lref);
329 /* table should have "foo/bar/" with trailing slash */
330 g_assert (lref[lref_len - 1] == '/');
332 if ((jbits & JURISDICTION_GENERIC)) {
333 GST_TRACE ("[%2d] %s checking generic match", i, licenses[i].ref);
336 if (strcmp (ref, lref) == 0)
339 /* exact match but without the trailing slash in ref? */
340 if (strncmp (ref, lref, lref_len - 1) == 0 && ref[lref_len - 1] == '\0')
344 if (!g_str_has_prefix (ref, lref))
347 GST_TRACE ("[%2d] %s checking jurisdictions", i, licenses[i].ref);
349 jbits &= ~JURISDICTION_GENERIC;
351 jur_suffix = ref + lref_len;
352 if (*jur_suffix == '\0')
355 jurs = jurisdictions;
357 guint jur_len = strlen (jurs);
360 if (strncmp (jur_suffix, jurs, jur_len) == 0 &&
361 (jur_suffix[jur_len] == '\0' || jur_suffix[jur_len] == '/')) {
362 GST_LOG ("matched %s to %s with jurisdiction %s (idx %d)",
363 license_ref, licenses[i].ref, jurs, i);
364 if (jurisdiction != NULL)
365 *jurisdiction = jurs;
369 g_assert (jurs < (jurisdictions + sizeof (jurisdictions)));
375 GST_WARNING ("unhandled license ref '%s'", license_ref);
380 * gst_tag_get_license_flags:
381 * @license_ref: a license reference string in form of a URI,
382 * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
384 * Get the flags of a license, which describe most of the features of
385 * a license in their most general form.
387 * Returns: the flags of the license, or 0 if the license is unknown
392 gst_tag_get_license_flags (const gchar * license_ref)
396 g_return_val_if_fail (license_ref != NULL, 0);
398 idx = gst_tag_get_license_idx (license_ref, NULL);
399 return (idx < 0) ? 0 : licenses[idx].flags;
403 * gst_tag_get_license_nick:
404 * @license_ref: a license reference string in form of a URI,
405 * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
407 * Get the nick name of a license, which is a short (untranslated) string
408 * such as e.g. "CC BY-NC-ND 2.0 UK".
410 * Returns: the nick name of the license, or NULL if the license is unknown
415 gst_tag_get_license_nick (const gchar * license_ref)
417 GstTagLicenseFlags flags;
418 const gchar *creator_prefix, *res;
421 g_return_val_if_fail (license_ref != NULL, NULL);
423 flags = gst_tag_get_license_flags (license_ref);
425 if ((flags & GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE)) {
426 creator_prefix = "CC ";
427 } else if ((flags & GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE)) {
428 creator_prefix = "FSF ";
429 } else if (g_str_has_suffix (license_ref, "publicdomain/")) {
435 nick = g_strdup_printf ("%s%s", creator_prefix,
436 license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1);
437 g_strdelimit (nick, "/", ' ');
439 for (c = nick; *c != '\0'; ++c)
440 *c = g_ascii_toupper (*c);
442 GST_LOG ("%s => nick %s", license_ref, nick);
443 res = g_intern_string (nick); /* for convenience */
450 * gst_tag_get_license_title:
451 * @license_ref: a license reference string in form of a URI,
452 * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
454 * Get the title of a license, which is a short translated description
455 * of the license's features (generally not very pretty though).
457 * Returns: the title of the license, or NULL if the license is unknown or
458 * no title is available.
463 gst_tag_get_license_title (const gchar * license_ref)
467 g_return_val_if_fail (license_ref != NULL, NULL);
469 idx = gst_tag_get_license_idx (license_ref, NULL);
471 if (idx < 0 || licenses[idx].title_idx < 0)
474 return gst_license_str_translate (&license_strings[licenses[idx].title_idx]);
478 * gst_tag_get_license_description:
479 * @license_ref: a license reference string in form of a URI,
480 * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
482 * Get the description of a license, which is a translated description
483 * of the license's main features.
485 * Returns: the description of the license, or NULL if the license is unknown
486 * or a description is not available.
491 gst_tag_get_license_description (const gchar * license_ref)
495 g_return_val_if_fail (license_ref != NULL, NULL);
497 idx = gst_tag_get_license_idx (license_ref, NULL);
499 if (idx < 0 || licenses[idx].desc_idx < 0)
502 return gst_license_str_translate (&license_strings[licenses[idx].desc_idx]);
506 * gst_tag_get_license_jurisdiction:
507 * @license_ref: a license reference string in form of a URI,
508 * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
510 * Get the jurisdiction code of a license. This is usually a two-letter
511 * ISO 3166-1 alpha-2 code, but there is also the special case of Scotland,
512 * for which no code exists and which is thus represented as "scotland".
514 * Known jurisdictions: ar, at, au, be, bg, br, ca, ch, cl, cn, co, de,
515 * dk, es, fi, fr, hr, hu, il, in, it, jp, kr, mk, mt, mx, my, nl, pe, pl,
516 * pt, scotland, se, si, tw, uk, us, za.
518 * Returns: the jurisdiction code of the license, or NULL if the license is
519 * unknown or is not specific to a particular jurisdiction.
524 gst_tag_get_license_jurisdiction (const gchar * license_ref)
526 const gchar *jurisdiction;
529 g_return_val_if_fail (license_ref != NULL, NULL);
531 idx = gst_tag_get_license_idx (license_ref, &jurisdiction);
532 return (idx < 0) ? NULL : jurisdiction;
536 * gst_tag_get_license_version:
537 * @license_ref: a license reference string in form of a URI,
538 * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/"
540 * Get the version of a license.
542 * Returns: the version of the license, or NULL if the license is not known or
548 gst_tag_get_license_version (const gchar * license_ref)
552 g_return_val_if_fail (license_ref != NULL, NULL);
554 idx = gst_tag_get_license_idx (license_ref, NULL);
558 #define LICENSE_FLAG_CC_OR_FSF \
559 (GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE|\
560 GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE)
562 /* e.g. publicdomain isn't versioned */
563 if (!(licenses[idx].flags & LICENSE_FLAG_CC_OR_FSF))
566 /* KISS for now... */
567 if (strstr (licenses[idx].ref, "/1.0/"))
569 else if (strstr (licenses[idx].ref, "/2.0/"))
571 else if (strstr (licenses[idx].ref, "/2.1/"))
573 else if (strstr (licenses[idx].ref, "/2.5/"))
575 else if (strstr (licenses[idx].ref, "/3.0/"))
578 GST_ERROR ("Could not determine version for ref '%s'", license_ref);
583 gst_tag_license_flags_get_type (void)
585 /* FIXME: we should really be using glib-mkenums for this.. */
586 #define C_FLAGS(v) ((guint) v)
588 static const GFlagsValue values[] = {
589 {C_FLAGS (GST_TAG_LICENSE_PERMITS_REPRODUCTION),
590 "GST_TAG_LICENSE_PERMITS_REPRODUCTION", "permits-reproduction"},
591 {C_FLAGS (GST_TAG_LICENSE_PERMITS_DISTRIBUTION),
592 "GST_TAG_LICENSE_PERMITS_DISTRIBUTION", "permits-distribution"},
593 {C_FLAGS (GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS),
594 "GST_TAG_LICENSE_PERMITS_DERIVATIVE_WORKS",
595 "permits-derivative-works"},
596 {C_FLAGS (GST_TAG_LICENSE_PERMITS_SHARING),
597 "GST_TAG_LICENSE_PERMITS_SHARING", "permits-sharing"},
598 {C_FLAGS (GST_TAG_LICENSE_REQUIRES_NOTICE),
599 "GST_TAG_LICENSE_REQUIRES_NOTICE", "requires-notice"},
600 {C_FLAGS (GST_TAG_LICENSE_REQUIRES_ATTRIBUTION),
601 "GST_TAG_LICENSE_REQUIRES_ATTRIBUTION", "requires-attributions"},
602 {C_FLAGS (GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE),
603 "GST_TAG_LICENSE_REQUIRES_SHARE_ALIKE", "requires-share-alike"},
604 {C_FLAGS (GST_TAG_LICENSE_REQUIRES_SOURCE_CODE),
605 "GST_TAG_LICENSE_REQUIRES_SOURCE_CODE", "requires-source-code"},
606 {C_FLAGS (GST_TAG_LICENSE_REQUIRES_COPYLEFT),
607 "GST_TAG_LICENSE_REQUIRES_COPYLEFT", "requires-copyleft"},
608 {C_FLAGS (GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT),
609 "GST_TAG_LICENSE_REQUIRES_LESSER_COPYLEFT",
610 "requires-lesser-copyleft"},
611 {C_FLAGS (GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE),
612 "GST_TAG_LICENSE_PROHIBITS_COMMERCIAL_USE",
613 "prohibits-commercial-use"},
614 {C_FLAGS (GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE),
615 "GST_TAG_LICENSE_PROHIBITS_HIGH_INCOME_NATION_USE",
616 "prohibits-high-income-nation-use"},
617 {C_FLAGS (GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE),
618 "GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE",
619 "creative-commons-license"},
620 {C_FLAGS (GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE),
621 "GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE",
622 "free-software-foundation-license"},
626 if (g_once_init_enter (&id)) {
627 GType tmp = g_flags_register_static ("GstTagLicenseFlags", values);
628 g_once_init_leave (&id, tmp);