tizen 2.0 init
[framework/multimedia/gst-plugins-base0.10.git] / gst-libs / gst / tag / licenses.c
1 /* GStreamer media licenses utility functions
2  * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /**
21  * SECTION:gsttaglicenses
22  * @short_description: utility functions for Creative Commons licenses
23  * @see_also: #GstTagList
24  *
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.
29  */
30
31 /* FIXME: add API to check obsolete-ness / replace-by */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <gst/gst.h>
38
39 #include <string.h>
40 #include <stdlib.h>
41
42 #include "tag.h"
43 #include "licenses-tables.dat"
44
45 #ifndef GST_DISABLE_GST_DEBUG
46
47 #define GST_CAT_DEFAULT ensure_debug_category()
48
49 static GstDebugCategory *
50 ensure_debug_category (void)
51 {
52   static gsize cat_gonce = 0;
53
54   if (g_once_init_enter (&cat_gonce)) {
55     gsize cat_done;
56
57     cat_done = (gsize) _gst_debug_category_new ("tag-licenses", 0,
58         "GstTag licenses");
59
60     g_once_init_leave (&cat_gonce, cat_done);
61   }
62
63   return (GstDebugCategory *) cat_gonce;
64 }
65
66 #else
67
68 #define ensure_debug_category() /* NOOP */
69
70 #endif /* GST_DISABLE_GST_DEBUG */
71
72 /* -------------------------------------------------------------------------
73  *  Translations
74  * ------------------------------------------------------------------------- */
75
76 #ifdef ENABLE_NLS
77 static GVariant *
78 gst_tag_get_license_translations_dictionary (void)
79 {
80   static gsize var_gonce = 0;
81
82   if (g_once_init_enter (&var_gonce)) {
83     const gchar *dict_path;
84     GVariant *var = NULL;
85     GError *err = NULL;
86     gchar *data;
87     gsize len;
88
89     /* for gst-uninstalled */
90     dict_path = g_getenv ("GST_TAG_LICENSE_TRANSLATIONS_DICT");
91
92     if (dict_path == NULL)
93       dict_path = LICENSE_TRANSLATIONS_PATH;
94
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);
99     } else {
100       GST_WARNING ("Could not load translation dictionary %s", err->message);
101       g_error_free (err);
102       var = g_variant_new_array (G_VARIANT_TYPE ("{sa{ss}}"), NULL, 0);
103     }
104
105     g_once_init_leave (&var_gonce, (gsize) var);
106   }
107
108   return (GVariant *) var_gonce;
109 }
110 #endif
111
112 #ifdef ENABLE_NLS
113
114 #if !GLIB_CHECK_VERSION(2,28,0)
115 static GVariant *
116 gst_g_variant_lookup_value (GVariant * dictionary, const gchar * key,
117     const GVariantType * expected_type)
118 {
119   GVariantIter iter;
120   GVariant *entry;
121   GVariant *value;
122
123   GST_ERROR ("here, using fallback");
124
125   g_assert (g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{s*}")));
126   g_assert (expected_type != NULL);
127
128   g_variant_iter_init (&iter, dictionary);
129   while ((entry = g_variant_iter_next_value (&iter))) {
130     GVariant *entry_key;
131     gboolean matches;
132
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);
136
137     if (matches)
138       break;
139
140     g_variant_unref (entry);
141   }
142
143   if (entry == NULL)
144     return NULL;
145
146   value = g_variant_get_child_value (entry, 1);
147   g_variant_unref (entry);
148
149   if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) {
150     GVariant *tmp;
151
152     tmp = g_variant_get_variant (value);
153     g_variant_unref (value);
154
155     if (expected_type && !g_variant_is_of_type (tmp, expected_type)) {
156       g_variant_unref (tmp);
157       tmp = NULL;
158     }
159
160     value = tmp;
161   }
162
163   g_assert (value == NULL || g_variant_is_of_type (value, expected_type));
164
165   return value;
166 }
167
168 #define g_variant_lookup_value gst_g_variant_lookup_value
169 #endif /* !GLIB_CHECK_VERSION(2,28,0) */
170
171 static gboolean
172 gst_variant_lookup_string_value (GVariant * dict, const gchar * lang,
173     const gchar ** translation)
174 {
175   GVariant *trans;
176
177   trans = g_variant_lookup_value (dict, lang, G_VARIANT_TYPE ("s"));
178   if (trans == NULL)
179     return FALSE;
180
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);
185   return TRUE;
186 }
187 #endif
188
189 static const gchar *
190 gst_license_str_translate (const gchar * s)
191 {
192 #ifdef ENABLE_NLS
193   GVariant *v, *dict, *trans;
194
195   v = gst_tag_get_license_translations_dictionary ();
196   g_assert (v != NULL);
197
198   dict = g_variant_lookup_value (v, s, G_VARIANT_TYPE ("a{ss}"));
199   if (dict != NULL) {
200     const gchar *const *lang;
201     const gchar *env_lang;
202
203     /* for unit tests */
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);
207       goto beach;
208     }
209
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"));
214
215       if (trans != NULL) {
216         s = g_variant_get_string (trans, NULL);
217         /* s will stay valid */
218         g_variant_unref (trans);
219         GST_TRACE ("Result: '%s'", s);
220         break;
221       }
222
223       GST_TRACE ("No result for '%s' for language '%s'", s, *lang);
224       ++lang;
225     }
226
227   beach:
228
229     g_variant_unref (dict);
230   } else {
231     GST_WARNING ("No dict for string '%s'", s);
232   }
233 #endif
234
235   return s;
236 }
237
238 /* -------------------------------------------------------------------------
239  *  License handling
240  * ------------------------------------------------------------------------- */
241
242 #define CC_LICENSE_REF_PREFIX "http://creativecommons.org/licenses/"
243
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)
247
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"
252     "uk\000us\000za";
253
254 /**
255  * gst_tag_get_licenses:
256  *
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).
261  *
262  * Returns: NULL-terminated array of license strings. Free with g_strfreev()
263  *     when no longer needed.
264  *
265  * Since: 0.10.36
266  */
267 gchar **
268 gst_tag_get_licenses (void)
269 {
270   GPtrArray *arr;
271   int i;
272
273   arr = g_ptr_array_new ();
274   for (i = 0; i < G_N_ELEMENTS (licenses); ++i) {
275     const gchar *jurs;
276     gboolean is_generic;
277     guint64 jbits;
278     gchar *ref;
279
280     jbits = licenses[i].jurisdictions;
281     is_generic = (jbits & JURISDICTION_GENERIC) != 0;
282     if (is_generic) {
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;
287     }
288
289     jurs = jurisdictions;
290     while (jbits != 0) {
291       if ((jbits & 1)) {
292         ref = g_strconcat (CC_LICENSE_REF_PREFIX, licenses[i].ref, jurs, "/",
293             NULL);
294         GST_LOG ("Adding %2d %s (%s: %s)", i, ref,
295             (is_generic) ? "derived" : "specific", jurs);
296         g_ptr_array_add (arr, ref);
297       }
298       g_assert (jurs < (jurisdictions + sizeof (jurisdictions)));
299       jurs += strlen (jurs) + 1;
300       jbits >>= 1;
301     }
302   }
303   g_ptr_array_add (arr, NULL);
304   return (gchar **) g_ptr_array_free (arr, FALSE);
305 }
306
307 static gint
308 gst_tag_get_license_idx (const gchar * license_ref, const gchar ** jurisdiction)
309 {
310   const gchar *ref, *jur_suffix;
311   int i;
312
313   GST_TRACE ("Looking up '%s'", license_ref);
314
315   if (!g_str_has_prefix (license_ref, CC_LICENSE_REF_PREFIX)) {
316     GST_WARNING ("unknown license prefix in ref '%s'", license_ref);
317     return -1;
318   }
319
320   if (jurisdiction != NULL)
321     *jurisdiction = NULL;
322
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);
328
329     /* table should have "foo/bar/" with trailing slash */
330     g_assert (lref[lref_len - 1] == '/');
331
332     if ((jbits & JURISDICTION_GENERIC)) {
333       GST_TRACE ("[%2d] %s checking generic match", i, licenses[i].ref);
334
335       /* exact match? */
336       if (strcmp (ref, lref) == 0)
337         return i;
338
339       /* exact match but without the trailing slash in ref? */
340       if (strncmp (ref, lref, lref_len - 1) == 0 && ref[lref_len - 1] == '\0')
341         return i;
342     }
343
344     if (!g_str_has_prefix (ref, lref))
345       continue;
346
347     GST_TRACE ("[%2d] %s checking jurisdictions", i, licenses[i].ref);
348
349     jbits &= ~JURISDICTION_GENERIC;
350
351     jur_suffix = ref + lref_len;
352     if (*jur_suffix == '\0')
353       continue;
354
355     jurs = jurisdictions;
356     while (jbits != 0) {
357       guint jur_len = strlen (jurs);
358
359       if ((jbits & 1)) {
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;
366           return i;
367         }
368       }
369       g_assert (jurs < (jurisdictions + sizeof (jurisdictions)));
370       jurs += jur_len + 1;
371       jbits >>= 1;
372     }
373   }
374
375   GST_WARNING ("unhandled license ref '%s'", license_ref);
376   return -1;
377 }
378
379 /**
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/"
383  *
384  * Get the flags of a license, which describe most of the features of
385  * a license in their most general form.
386  *
387  * Returns: the flags of the license, or 0 if the license is unknown
388  *
389  * Since: 0.10.36
390  */
391 GstTagLicenseFlags
392 gst_tag_get_license_flags (const gchar * license_ref)
393 {
394   int idx;
395
396   g_return_val_if_fail (license_ref != NULL, 0);
397
398   idx = gst_tag_get_license_idx (license_ref, NULL);
399   return (idx < 0) ? 0 : licenses[idx].flags;
400 }
401
402 /**
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/"
406  *
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".
409  *
410  * Returns: the nick name of the license, or NULL if the license is unknown
411  *
412  * Since: 0.10.36
413  */
414 const gchar *
415 gst_tag_get_license_nick (const gchar * license_ref)
416 {
417   GstTagLicenseFlags flags;
418   const gchar *creator_prefix, *res;
419   gchar *nick, *c;
420
421   g_return_val_if_fail (license_ref != NULL, NULL);
422
423   flags = gst_tag_get_license_flags (license_ref);
424
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/")) {
430     creator_prefix = "";
431   } else {
432     return NULL;
433   }
434
435   nick = g_strdup_printf ("%s%s", creator_prefix,
436       license_ref + sizeof (CC_LICENSE_REF_PREFIX) - 1);
437   g_strdelimit (nick, "/", ' ');
438   g_strchomp (nick);
439   for (c = nick; *c != '\0'; ++c)
440     *c = g_ascii_toupper (*c);
441
442   GST_LOG ("%s => nick %s", license_ref, nick);
443   res = g_intern_string (nick); /* for convenience */
444   g_free (nick);
445
446   return res;
447 }
448
449 /**
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/"
453  *
454  * Get the title of a license, which is a short translated description
455  * of the license's features (generally not very pretty though).
456  *
457  * Returns: the title of the license, or NULL if the license is unknown or
458  *    no title is available.
459  *
460  * Since: 0.10.36
461  */
462 const gchar *
463 gst_tag_get_license_title (const gchar * license_ref)
464 {
465   int idx;
466
467   g_return_val_if_fail (license_ref != NULL, NULL);
468
469   idx = gst_tag_get_license_idx (license_ref, NULL);
470
471   if (idx < 0 || licenses[idx].title_idx < 0)
472     return NULL;
473
474   return gst_license_str_translate (&license_strings[licenses[idx].title_idx]);
475 }
476
477 /**
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/"
481  *
482  * Get the description of a license, which is a translated description
483  * of the license's main features.
484  *
485  * Returns: the description of the license, or NULL if the license is unknown
486  *    or a description is not available.
487  *
488  * Since: 0.10.36
489  */
490 const gchar *
491 gst_tag_get_license_description (const gchar * license_ref)
492 {
493   int idx;
494
495   g_return_val_if_fail (license_ref != NULL, NULL);
496
497   idx = gst_tag_get_license_idx (license_ref, NULL);
498
499   if (idx < 0 || licenses[idx].desc_idx < 0)
500     return NULL;
501
502   return gst_license_str_translate (&license_strings[licenses[idx].desc_idx]);
503 }
504
505 /**
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/"
509  *
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".
513  *
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.
517  *
518  * Returns: the jurisdiction code of the license, or NULL if the license is
519  *    unknown or is not specific to a particular jurisdiction.
520  *
521  * Since: 0.10.36
522  */
523 const gchar *
524 gst_tag_get_license_jurisdiction (const gchar * license_ref)
525 {
526   const gchar *jurisdiction;
527   int idx;
528
529   g_return_val_if_fail (license_ref != NULL, NULL);
530
531   idx = gst_tag_get_license_idx (license_ref, &jurisdiction);
532   return (idx < 0) ? NULL : jurisdiction;
533 }
534
535 /**
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/"
539  *
540  * Get the version of a license.
541  *
542  * Returns: the version of the license, or NULL if the license is not known or
543  *    has no version
544  *
545  * Since: 0.10.36
546  */
547 const gchar *
548 gst_tag_get_license_version (const gchar * license_ref)
549 {
550   int idx;
551
552   g_return_val_if_fail (license_ref != NULL, NULL);
553
554   idx = gst_tag_get_license_idx (license_ref, NULL);
555   if (idx < 0)
556     return NULL;
557
558 #define LICENSE_FLAG_CC_OR_FSF \
559  (GST_TAG_LICENSE_CREATIVE_COMMONS_LICENSE|\
560   GST_TAG_LICENSE_FREE_SOFTWARE_FOUNDATION_LICENSE)
561
562   /* e.g. publicdomain isn't versioned */
563   if (!(licenses[idx].flags & LICENSE_FLAG_CC_OR_FSF))
564     return NULL;
565
566   /* KISS for now... */
567   if (strstr (licenses[idx].ref, "/1.0/"))
568     return "1.0";
569   else if (strstr (licenses[idx].ref, "/2.0/"))
570     return "2.0";
571   else if (strstr (licenses[idx].ref, "/2.1/"))
572     return "2.1";
573   else if (strstr (licenses[idx].ref, "/2.5/"))
574     return "2.5";
575   else if (strstr (licenses[idx].ref, "/3.0/"))
576     return "3.0";
577
578   GST_ERROR ("Could not determine version for ref '%s'", license_ref);
579   return NULL;
580 }
581
582 GType
583 gst_tag_license_flags_get_type (void)
584 {
585   /* FIXME: we should really be using glib-mkenums for this.. */
586 #define C_FLAGS(v) ((guint) v)
587   static gsize id = 0;
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"},
623     {0, NULL, NULL}
624   };
625
626   if (g_once_init_enter (&id)) {
627     GType tmp = g_flags_register_static ("GstTagLicenseFlags", values);
628     g_once_init_leave (&id, tmp);
629   }
630
631   return (GType) id;
632 }