| /* GStreamer media licenses utility functions |
| * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| /** |
| * SECTION:gsttaglicenses |
| * @title: Licenses |
| * @short_description: utility functions for Creative Commons licenses |
| * @see_also: #GstTagList |
| * |
| * Provides information about Creative Commons media licenses, which are |
| * often expressed in media files as a license URI in tags. Also useful |
| * for applications creating media files, in case the user wants to license |
| * the content under a Creative Commons license. |
| */ |
| |
| /* FIXME: add API to check obsolete-ness / replace-by */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/gst.h> |
| |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #include "tag.h" |
| #include "licenses-tables.dat" |
| |
| #ifndef GST_DISABLE_GST_DEBUG |
| |
| #define GST_CAT_DEFAULT ensure_debug_category() |
| |
| static GstDebugCategory * |
| ensure_debug_category (void) |
| { |
| static gsize cat_gonce = 0; |
| |
| if (g_once_init_enter (&cat_gonce)) { |
| gsize cat_done; |
| |
| cat_done = (gsize) _gst_debug_category_new ("tag-licenses", 0, |
| "GstTag licenses"); |
| |
| g_once_init_leave (&cat_gonce, cat_done); |
| } |
| |
| return (GstDebugCategory *) cat_gonce; |
| } |
| |
| #else |
| |
| #define ensure_debug_category() /* NOOP */ |
| |
| #endif /* GST_DISABLE_GST_DEBUG */ |
| |
| /* ------------------------------------------------------------------------- |
| * Translations |
| * ------------------------------------------------------------------------- */ |
| |
| #ifdef ENABLE_NLS |
| static GVariant * |
| gst_tag_get_license_translations_dictionary (void) |
| { |
| static gsize var_gonce = 0; |
| |
| if (g_once_init_enter (&var_gonce)) { |
| const gchar *dict_path; |
| GVariant *var = NULL; |
| GError *err = NULL; |
| gchar *data; |
| gsize len; |
| |
| /* for gst-uninstalled */ |
| dict_path = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_DICT"); |
| |
| if (dict_path == NULL) |
| dict_path = LICENSE_TRANSLATIONS_PATH; |
| |
| GST_INFO ("Loading license translations from '%s'", dict_path); |
| if (g_file_get_contents (dict_path, &data, &len, &err)) { |
| var = g_variant_new_from_data (G_VARIANT_TYPE ("a{sa{ss}}"), data, len, |
| TRUE, (GDestroyNotify) g_free, data); |
| } else { |
| GST_WARNING ("Could not load translation dictionary %s", err->message); |
| g_error_free (err); |
| var = g_variant_new_array (G_VARIANT_TYPE ("{sa{ss}}"), NULL, 0); |
| } |
| |
| g_once_init_leave (&var_gonce, (gsize) var); |
| } |
| |
| return (GVariant *) var_gonce; |
| } |
| #endif |
| |
| #ifdef ENABLE_NLS |
| static gboolean |
| gst_variant_lookup_string_value (GVariant * dict, const gchar * lang, |
| const gchar ** translation) |
| { |
| GVariant *trans; |
| |
| trans = g_variant_lookup_value (dict, lang, G_VARIANT_TYPE ("s")); |
| if (trans == NULL) |
| return FALSE; |
| |
| *translation = g_variant_get_string (trans, NULL); |
| /* string will stay valid */ |
| g_variant_unref (trans); |
| GST_TRACE ("Result: '%s' for language '%s'", *translation, lang); |
| return TRUE; |
| } |
| #endif |
| |
| static const gchar * |
| gst_license_str_translate (const gchar * s) |
| { |
| #ifdef ENABLE_NLS |
| GVariant *v, *dict, *trans; |
| |
| v = gst_tag_get_license_translations_dictionary (); |
| g_assert (v != NULL); |
| |
| dict = g_variant_lookup_value (v, s, G_VARIANT_TYPE ("a{ss}")); |
| if (dict != NULL) { |
| const gchar *const *lang; |
| const gchar *env_lang; |
| |
| /* for unit tests */ |
| if ((env_lang = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_LANG"))) { |
| if (gst_variant_lookup_string_value (dict, env_lang, &s)) |
| GST_TRACE ("Result: '%s' for forced language '%s'", s, env_lang); |
| goto beach; |
| } |
| |
| lang = g_get_language_names (); |
| while (lang != NULL && *lang != NULL) { |
| GST_TRACE ("Looking up '%s' for language '%s'", s, *lang); |
| trans = g_variant_lookup_value (dict, *lang, G_VARIANT_TYPE ("s")); |
| |
| if (trans != NULL) { |
| s = g_variant_get_string (trans, NULL); |
| /* s will stay valid */ |
| g_variant_unref (trans); |
| GST_TRACE ("Result: '%s'", s); |
| break; |
| } |
| |
| GST_TRACE ("No result for '%s' for language '%s'", s, *lang); |
| ++lang; |
| } |
| |
| beach: |
| |
| g_variant_unref (dict); |
| } else { |
| GST_WARNING ("No dict for string '%s'", s); |
| } |
| #endif |
| |
| return s; |
| } |
| |
| /* ------------------------------------------------------------------------- |
| * License handling |
| * ------------------------------------------------------------------------- */ |
| |
| #define CC_LICENSE_REF_PREFIX "http://creativecommons.org/licenses/" |
| |
| /* is this license 'generic' (and a base for any of the supported |
| * jurisdictions), or jurisdiction-specific only? */ |
| #define JURISDICTION_GENERIC (G_GUINT64_CONSTANT (1) << 63) |
| |
| static const gchar jurisdictions[] = |
| "ar\000at\000au\000be\000bg\000br\000ca\000ch\000cl\000cn\000co\000de\000" |
| "dk\000es\000fi\000fr\000hr\000hu\000il\000in\000it\000jp\000kr\000mk\000" |
| "mt\000mx\000my\000nl\000pe\000pl\000pt\000scotland\000se\000si\000tw\000" |
| "uk\000us\000za"; |
| |
| /** |
| * gst_tag_get_licenses: |
| * |
| * Returns a list of known license references (in form of URIs). This is |
| * useful for UIs to build a list of available licenses for tagging purposes |
| * (e.g. to tag an audio track appropriately in a video or audio editor, or |
| * an image in a camera application). |
| * |
| * Returns: (transfer full): NULL-terminated array of license strings. Free |
| * with g_strfreev() when no longer needed. |
| */ |
| gchar ** |
| gst_tag_get_licenses (void) |
| { |
| GPtrArray *arr; |
| int i; |
| |
| arr = g_ptr_array_new (); |
| for (i = 0; i < G_N_ELEMENTS (licenses); ++i) { |
| const gchar *jurs; |
| gboolean is_generic; |
| guint64 jbits; |
| gchar *ref; |
| |
| jbits = licenses[i].jurisdictions; |
| is_generic = (jbits & JURISDICTION_GENERIC) != 0; |
| if (is_generic) { |
| ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, NULL); |
| GST_LOG ("Adding %2d %s (generic)", i, ref); |
| g_ptr_array_add (arr, ref); |
| jbits &= ~JURISDICTION_GENERIC; |
| } |
| |
| jurs = jurisdictions; |
| while (jbits != 0) { |
| if ((jbits & 1)) { |
| ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, jurs, "/", |
| NULL); |
| GST_LOG ("Adding %2d %s (%s: %s)", i, ref, |
| (is_generic) ? "derived" : "specific", jurs); |
| g_ptr_array_add (arr, ref); |
| } |
| g_assert (jurs < (jurisdictions + sizeof (jurisdictions))); |
| jurs += strlen (jurs) + 1; |
| jbits >>= 1; |
| } |
| } |
| g_ptr_array_add (arr, NULL); |
| return (gchar **) g_ptr_array_free (arr, FALSE); |
| } |
| |
| static gint |
| gst_tag_get_license_idx (const gchar * license_ref, const gchar ** jurisdiction) |
| { |
| const gchar *ref, *jur_suffix; |
| int i; |
| |
| GST_TRACE ("Looking up '%s'", license_ref); |
| |
| if (!g_str_has_prefix (license_ref, CC_LICENSE_REF_PREFIX)) { |
| GST_WARNING ("unknown license prefix in ref '%s'", license_ref); |
| return -1; |
| } |
| |
| if (jurisdiction != NULL) |
| *jurisdiction = NULL; |
| |
| ref = license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1; |
| for (i = 0; i < G_N_ELEMENTS (licenses); ++i) { |
| guint64 jbits = licenses[i].jurisdictions; |
| const gchar *jurs, *lref = licenses[i].ref; |
| gsize lref_len = strlen (lref); |
| |
| /* table should have "foo/bar/" with trailing slash */ |
| g_assert (lref[lref_len - 1] == '/'); |
| |
| if ((jbits & JURISDICTION_GENERIC)) { |
| GST_TRACE ("[%2d] %s checking generic match", i, licenses[i].ref); |
| |
| /* exact match? */ |
| if (strcmp (ref, lref) == 0) |
| return i; |
| |
| /* exact match but without the trailing slash in ref? */ |
| if (strncmp (ref, lref, lref_len - 1) == 0 && ref[lref_len - 1] == '\0') |
| return i; |
| } |
| |
| if (!g_str_has_prefix (ref, lref)) |
| continue; |
| |
| GST_TRACE ("[%2d] %s checking jurisdictions", i, licenses[i].ref); |
| |
| jbits &= ~JURISDICTION_GENERIC; |
| |
| jur_suffix = ref + lref_len; |
| if (*jur_suffix == '\0') |
| continue; |
| |
| jurs = jurisdictions; |
| while (jbits != 0) { |
| guint jur_len = strlen (jurs); |
| |
| if ((jbits & 1)) { |
| if (strncmp (jur_suffix, jurs, jur_len) == 0 && |
| (jur_suffix[jur_len] == '\0' || jur_suffix[jur_len] == '/')) { |
| GST_LOG ("matched %s to %s with jurisdiction %s (idx %d)", |
| license_ref, licenses[i].ref, jurs, i); |
| if (jurisdiction != NULL) |
| *jurisdiction = jurs; |
| return i; |
| } |
| } |
| g_assert (jurs < (jurisdictions + sizeof (jurisdictions))); |
| jurs += jur_len + 1; |
| jbits >>= 1; |
| } |
| } |
| |
| GST_WARNING ("unhandled license ref '%s'", license_ref); |
| return -1; |
| } |
| |
| /** |
| * gst_tag_get_license_flags: |
| * @license_ref: a license reference string in form of a URI, |
| * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/" |
| * |
| * Get the flags of a license, which describe most of the features of |
| * a license in their most general form. |
| * |
| * Returns: the flags of the license, or 0 if the license is unknown |
| */ |
| GstTagLicenseFlags |
| gst_tag_get_license_flags (const gchar * license_ref) |
| { |
| int idx; |
| |
| g_return_val_if_fail (license_ref != NULL, 0); |
| |
| idx = gst_tag_get_license_idx (license_ref, NULL); |
| return (idx < 0) ? 0 : licenses[idx].flags; |
| } |
| |
| /** |
| * gst_tag_get_license_nick: |
| * @license_ref: a license reference string in form of a URI, |
| * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/" |
| * |
| * Get the nick name of a license, which is a short (untranslated) string |
| * such as e.g. "CC BY-NC-ND 2.0 UK". |
| * |
| * Returns: the nick name of the license, or NULL if the license is unknown |
| */ |
| const gchar * |
| gst_tag_get_license_nick (const gchar * license_ref) |
| { |
| GstTagLicenseFlags flags; |
| const gchar *creator_prefix, *res; |
| gchar *nick, *c; |
| |
| g_return_val_if_fail (license_ref != NULL, NULL); |
| |
| flags = gst_tag_get_license_flags (license_ref); |
| |
| if ((flags & GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE)) { |
| creator_prefix = "CC "; |
| } else if ((flags & GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE)) { |
| creator_prefix = "FSF "; |
| } else if (g_str_has_suffix (license_ref, "publicdomain/")) { |
| creator_prefix = ""; |
| } else { |
| return NULL; |
| } |
| |
| nick = g_strdup_printf ("%s%s", creator_prefix, |
| license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1); |
| g_strdelimit (nick, "/", ' '); |
| g_strchomp (nick); |
| for (c = nick; *c != '\0'; ++c) |
| *c = g_ascii_toupper (*c); |
| |
| GST_LOG ("%s => nick %s", license_ref, nick); |
| res = g_intern_string (nick); /* for convenience */ |
| g_free (nick); |
| |
| return res; |
| } |
| |
| /** |
| * gst_tag_get_license_title: |
| * @license_ref: a license reference string in form of a URI, |
| * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/" |
| * |
| * Get the title of a license, which is a short translated description |
| * of the license's features (generally not very pretty though). |
| * |
| * Returns: the title of the license, or NULL if the license is unknown or |
| * no title is available. |
| */ |
| const gchar * |
| gst_tag_get_license_title (const gchar * license_ref) |
| { |
| int idx; |
| |
| g_return_val_if_fail (license_ref != NULL, NULL); |
| |
| idx = gst_tag_get_license_idx (license_ref, NULL); |
| |
| if (idx < 0 || licenses[idx].title_idx < 0) |
| return NULL; |
| |
| return gst_license_str_translate (&license_strings[licenses[idx].title_idx]); |
| } |
| |
| /** |
| * gst_tag_get_license_description: |
| * @license_ref: a license reference string in form of a URI, |
| * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/" |
| * |
| * Get the description of a license, which is a translated description |
| * of the license's main features. |
| * |
| * Returns: the description of the license, or NULL if the license is unknown |
| * or a description is not available. |
| */ |
| const gchar * |
| gst_tag_get_license_description (const gchar * license_ref) |
| { |
| int idx; |
| |
| g_return_val_if_fail (license_ref != NULL, NULL); |
| |
| idx = gst_tag_get_license_idx (license_ref, NULL); |
| |
| if (idx < 0 || licenses[idx].desc_idx < 0) |
| return NULL; |
| |
| return gst_license_str_translate (&license_strings[licenses[idx].desc_idx]); |
| } |
| |
| /** |
| * gst_tag_get_license_jurisdiction: |
| * @license_ref: a license reference string in form of a URI, |
| * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/" |
| * |
| * Get the jurisdiction code of a license. This is usually a two-letter |
| * ISO 3166-1 alpha-2 code, but there is also the special case of Scotland, |
| * for which no code exists and which is thus represented as "scotland". |
| * |
| * Known jurisdictions: ar, at, au, be, bg, br, ca, ch, cl, cn, co, de, |
| * dk, es, fi, fr, hr, hu, il, in, it, jp, kr, mk, mt, mx, my, nl, pe, pl, |
| * pt, scotland, se, si, tw, uk, us, za. |
| * |
| * Returns: the jurisdiction code of the license, or NULL if the license is |
| * unknown or is not specific to a particular jurisdiction. |
| */ |
| const gchar * |
| gst_tag_get_license_jurisdiction (const gchar * license_ref) |
| { |
| const gchar *jurisdiction; |
| int idx; |
| |
| g_return_val_if_fail (license_ref != NULL, NULL); |
| |
| idx = gst_tag_get_license_idx (license_ref, &jurisdiction); |
| return (idx < 0) ? NULL : jurisdiction; |
| } |
| |
| /** |
| * gst_tag_get_license_version: |
| * @license_ref: a license reference string in form of a URI, |
| * e.g. "http://creativecommons.org/licenses/by-nc-nd/2.0/" |
| * |
| * Get the version of a license. |
| * |
| * Returns: the version of the license, or NULL if the license is not known or |
| * has no version |
| */ |
| const gchar * |
| gst_tag_get_license_version (const gchar * license_ref) |
| { |
| int idx; |
| |
| g_return_val_if_fail (license_ref != NULL, NULL); |
| |
| idx = gst_tag_get_license_idx (license_ref, NULL); |
| if (idx < 0) |
| return NULL; |
| |
| #define LICENSE_FLAG_CC_OR_FSF \ |
| (GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE|\ |
| GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE) |
| |
| /* e.g. publicdomain isn't versioned */ |
| if (!(licenses[idx].flags & LICENSE_FLAG_CC_OR_FSF)) |
| return NULL; |
| |
| /* KISS for now... */ |
| if (strstr (licenses[idx].ref, "/1.0/")) |
| return "1.0"; |
| else if (strstr (licenses[idx].ref, "/2.0/")) |
| return "2.0"; |
| else if (strstr (licenses[idx].ref, "/2.1/")) |
| return "2.1"; |
| else if (strstr (licenses[idx].ref, "/2.5/")) |
| return "2.5"; |
| else if (strstr (licenses[idx].ref, "/3.0/")) |
| return "3.0"; |
| |
| GST_ERROR ("Could not determine version for ref '%s'", license_ref); |
| return NULL; |
| } |