1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * stream metainfo helper functions
21  * hide some xine engine details from demuxers and reduce code duplication
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <pthread.h>
32 
33 #ifdef HAVE_ICONV
34 #  include <iconv.h>
35 #endif
36 
37 #include <xine/info_helper.h>
38 #include "xine_private.h"
39 
40 /* *******************  Stream Info  *************************** */
41 
42 /*
43  * Check if 'info' is in bounds.
44  */
info_valid(xine_stream_private_t * stream,int info)45 static int info_valid (xine_stream_private_t *stream, int info) {
46   if ((info >= 0) && (info < XINE_STREAM_INFO_MAX))
47     return 1;
48   else {
49     xprintf (stream->s.xine, XINE_VERBOSITY_DEBUG,
50       "info_helper: invalid STREAM_INFO %d. Ignored.\n", info);
51     return 0;
52   }
53 }
54 
55 /*
56  * Reset private info.
57  */
_x_stream_info_reset(xine_stream_t * s,int info)58 void _x_stream_info_reset (xine_stream_t *s, int info) {
59   xine_stream_private_t *stream = (xine_stream_private_t *)s;
60   stream = stream->side_streams[0];
61   if (info_valid (stream, info)) {
62     xine_rwlock_wrlock (&stream->info_lock);
63     stream->stream_info[info] = 0;
64     xine_rwlock_unlock (&stream->info_lock);
65   }
66 }
67 
68 /*
69  * Reset public info value.
70  */
_x_stream_info_public_reset(xine_stream_t * s,int info)71 void _x_stream_info_public_reset (xine_stream_t *s, int info) {
72   (void)s;
73   (void)info;
74 }
75 
76 /*
77  * Set private info value.
78  */
_x_stream_info_set(xine_stream_t * s,int info,int value)79 void _x_stream_info_set (xine_stream_t *s, int info, int value) {
80   xine_stream_private_t *stream = (xine_stream_private_t *)s, *m;
81   m = stream->side_streams[0];
82   if (info_valid (m, info)) {
83     xine_rwlock_wrlock (&m->info_lock);
84     if ((m != stream) &&
85       ((info == XINE_STREAM_INFO_HAS_CHAPTERS) ||
86        (info == XINE_STREAM_INFO_HAS_VIDEO) ||
87        (info == XINE_STREAM_INFO_HAS_AUDIO))) {
88       if (m->stream_info[info] == 0)
89         m->stream_info[info] = value;
90     } else {
91       m->stream_info[info] = value;
92     }
93     xine_rwlock_unlock (&m->info_lock);
94   }
95 }
96 
97 /*
98  * Retrieve private info value.
99  */
_x_stream_info_get(xine_stream_t * s,int info)100 uint32_t _x_stream_info_get (xine_stream_t *s, int info) {
101   xine_stream_private_t *stream = (xine_stream_private_t *)s;
102   uint32_t stream_info;
103   stream = stream->side_streams[0];
104   xine_rwlock_rdlock (&stream->info_lock);
105   stream_info = stream->stream_info[info];
106   xine_rwlock_unlock (&stream->info_lock);
107   return stream_info;
108 }
109 
110 /*
111  * Retrieve public info value
112  */
_x_stream_info_get_public(xine_stream_t * s,int info)113 uint32_t _x_stream_info_get_public (xine_stream_t *s, int info) {
114   xine_stream_private_t *stream = (xine_stream_private_t *)s;
115   uint32_t stream_info;
116   stream = stream->side_streams[0];
117   xine_rwlock_rdlock (&stream->info_lock);
118   stream_info = stream->stream_info[info];
119   xine_rwlock_unlock (&stream->info_lock);
120   return stream_info;
121 }
122 
123 /* ****************  Meta Info  *********************** */
124 
125 /*
126  * Remove trailing separator chars (\n,\r,\t, space,...)
127  * at the end of the string
128  */
meta_info_chomp(char * str)129 static void meta_info_chomp(char *str) {
130   ssize_t i, len;
131 
132   len = strlen(str);
133   if (!len)
134     return;
135   i = len - 1;
136 
137   while ((i >= 0) && ((unsigned char)str[i] <= 32)) {
138     str[i] = 0;
139     i--;
140   }
141 }
142 
143 /*
144  * Check if 'info' is in bounds.
145  */
meta_valid(xine_stream_private_t * stream,int info)146 static int meta_valid (xine_stream_private_t *stream, int info) {
147   if ((info >= 0) && (info < XINE_STREAM_INFO_MAX))
148     return 1;
149   else {
150     xprintf (stream->s.xine, XINE_VERBOSITY_DEBUG,
151       "info_helper: invalid META_INFO %d. Ignored.\n", info);
152     return 0;
153   }
154 }
155 
156 /*
157  * Set private meta info to utf-8 string value (can be NULL).
158  */
_meta_info_set_utf8(xine_stream_private_t * stream,int info,const char * value)159 static void _meta_info_set_utf8 (xine_stream_private_t *stream, int info, const char *value) {
160   if (meta_valid (stream, info)) {
161     xine_rwlock_wrlock (&stream->meta_lock);
162     if  (( value && !stream->meta_info[info])
163       || ( value &&  stream->meta_info[info] && strcmp (value, stream->meta_info[info]))
164       || (!value &&  stream->meta_info[info])) {
165       if (stream->meta_info_public[info] != stream->meta_info[info])
166         free (stream->meta_info[info]);
167       stream->meta_info[info] = (value) ? strdup(value) : NULL;
168       if (stream->meta_info[info])
169         meta_info_chomp (stream->meta_info[info]);
170     }
171     xine_rwlock_unlock (&stream->meta_lock);
172   }
173 }
174 
175 #ifdef HAVE_ICONV
is_utf8(const char * s)176 static int is_utf8 (const char *s) {
177   const uint8_t *p = (const uint8_t *)s;
178   while (1) {
179     if ((*p & 0x80) == 0x00) {
180       if (*p == 0x00)
181         break;
182       p += 1;
183     } else if ((*p & 0xe0) == 0xc0) {
184       if ((p[1] & 0xc0) != 0x80)
185         return -1;
186       p += 2;
187     } else if ((*p & 0xf0) == 0xe0) {
188       if  (((p[1] & 0xc0) != 0x80)
189         || ((p[2] & 0xc0) != 0x80))
190         return -1;
191       p += 3;
192     } else if ((*p & 0xf8) == 0xf0) {
193       if  (((p[1] & 0xc0) != 0x80)
194         || ((p[2] & 0xc0) != 0x80)
195         || ((p[3] & 0xc0) != 0x80))
196         return -1;
197       p += 4;
198     } else if ((*p & 0xfc) == 0xf8) {
199       if  (((p[1] & 0xc0) != 0x80)
200         || ((p[2] & 0xc0) != 0x80)
201         || ((p[3] & 0xc0) != 0x80)
202         || ((p[4] & 0xc0) != 0x80))
203         return -1;
204       p += 5;
205     } else if ((*p & 0xfe) == 0xfc) {
206       if  (((p[1] & 0xc0) != 0x80)
207         || ((p[2] & 0xc0) != 0x80)
208         || ((p[3] & 0xc0) != 0x80)
209         || ((p[4] & 0xc0) != 0x80)
210         || ((p[5] & 0xc0) != 0x80))
211         return -1;
212       p += 6;
213     } else {
214       return -1;
215     }
216   }
217   return p - (const uint8_t *)s;
218 }
219 #endif
220 
221 /*
222  * Set private meta info to value (can be NULL) with a given encoding.
223  * if encoding is NULL assume locale.
224  */
_meta_info_set_encoding(xine_stream_private_t * stream,int info,const char * value,const char * enc)225 static void _meta_info_set_encoding (xine_stream_private_t *stream, int info, const char *value, const char *enc) {
226   const char *buf_set = value;
227   char *buf_free = NULL;
228 
229 #ifdef HAVE_ICONV
230   char *system_enc = NULL;
231   iconv_t cd = (iconv_t)-1;
232 
233   do {
234     if (!value)
235       break;
236 
237     if (enc == NULL) {
238       if ((enc = system_enc = xine_get_system_encoding()) == NULL) {
239         xprintf (stream->s.xine, XINE_VERBOSITY_LOG,
240           _("info_helper: can't find out current locale character set\n"));
241         break;
242       }
243     }
244 
245     if (strcmp (enc, "UTF-8")) {
246       /* Don't bother converting if it's already in UTF-8, but the encoding
247        * is badly reported */
248       if (is_utf8 (value) >= 0)
249         break;
250     }
251 
252     cd = iconv_open ("UTF-8", enc);
253     if (cd == (iconv_t)-1) {
254       xprintf (stream->s.xine, XINE_VERBOSITY_LOG,
255         _("info_helper: unsupported conversion %s -> UTF-8, no conversion performed\n"), enc);
256       break;
257     }
258 
259     {
260       ICONV_CONST char *inbuf;
261       char *outbuf;
262       size_t inbytesleft, outbytesleft;
263 
264       if (!strncmp (enc, "UTF-16", 6) || !strncmp (enc, "UCS-2", 5)) {
265         /* strlen() won't work with UTF-16* or UCS-2* */
266         inbytesleft = 0;
267         while (value[inbytesleft] || value[inbytesleft + 1])
268           inbytesleft += 2;
269       } /* ... do we need to handle UCS-4? Probably not. */
270       else
271         inbytesleft = strlen(value);
272       outbytesleft = 4 * inbytesleft; /* estimative (max) */
273       buf_free = malloc (outbytesleft + 1);
274       if (!buf_free)
275         break;
276 
277       inbuf = (ICONV_CONST char *)value;
278       outbuf = buf_free;
279       if (iconv (cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)-1) {
280         *outbuf = '\0';
281         buf_set = buf_free;
282       }
283     }
284   } while (0);
285 
286   if (cd != (iconv_t)-1)
287     iconv_close (cd);
288   free (system_enc);
289 #endif
290 
291   _meta_info_set_utf8 (stream, info, buf_set);
292   free (buf_free);
293 }
294 
295 /*
296  * Reset (nullify) private info value.
297  */
_x_meta_info_reset(xine_stream_t * s,int info)298 void _x_meta_info_reset (xine_stream_t *s, int info) {
299   xine_stream_private_t *stream = (xine_stream_private_t *)s;
300   stream = stream->side_streams[0];
301   _meta_info_set_utf8 (stream, info, NULL);
302 }
303 
304 /*
305  * Reset (nullify) public info value.
306  */
_x_meta_info_public_reset(xine_stream_t * s,int info)307 void _x_meta_info_public_reset (xine_stream_t *s, int info) {
308   xine_stream_private_t *stream = (xine_stream_private_t *)s;
309   stream = stream->side_streams[0];
310   if (meta_valid (stream, info)) {
311     xine_rwlock_wrlock (&stream->meta_lock);
312     if (stream->meta_info_public[info] != stream->meta_info[info])
313       _x_freep (&stream->meta_info[info]);
314     xine_rwlock_unlock (&stream->meta_lock);
315   }
316 }
317 
318 /*
319  * Set private meta info value using current locale.
320  */
_x_meta_info_set(xine_stream_t * s,int info,const char * str)321 void _x_meta_info_set (xine_stream_t *s, int info, const char *str) {
322   xine_stream_private_t *stream = (xine_stream_private_t *)s;
323   stream = stream->side_streams[0];
324   if (str)
325     _meta_info_set_encoding (stream, info, str, NULL);
326 }
327 
328 /*
329  * Set private meta info value using specified encoding.
330  */
_x_meta_info_set_generic(xine_stream_t * s,int info,const char * str,const char * enc)331 void _x_meta_info_set_generic (xine_stream_t *s, int info, const char *str, const char *enc) {
332   xine_stream_private_t *stream = (xine_stream_private_t *)s;
333   stream = stream->side_streams[0];
334   if (str)
335     _meta_info_set_encoding (stream, info, str, enc);
336 }
337 
338 /*
339  * Set private meta info value using utf8.
340  */
_x_meta_info_set_utf8(xine_stream_t * s,int info,const char * str)341 void _x_meta_info_set_utf8 (xine_stream_t *s, int info, const char *str) {
342   xine_stream_private_t *stream = (xine_stream_private_t *)s;
343   stream = stream->side_streams[0];
344   if (str)
345     _meta_info_set_utf8 (stream, info, str);
346 }
347 
348 /*
349  * Set private meta info from buf, 'len' bytes long.
350  */
_x_meta_info_n_set(xine_stream_t * s,int info,const char * buf,int len)351 void _x_meta_info_n_set (xine_stream_t *s, int info, const char *buf, int len) {
352   xine_stream_private_t *stream = (xine_stream_private_t *)s;
353   stream = stream->side_streams[0];
354   if (meta_valid (stream, info) && len) {
355     char *str = strndup (buf, len);
356     _meta_info_set_encoding (stream, info, str, NULL);
357     free(str);
358   }
359 }
360 
361 /*
362  * Set private meta info value, from multiple arguments.
363  */
_x_meta_info_set_multi(xine_stream_t * s,int info,...)364 void _x_meta_info_set_multi (xine_stream_t *s, int info, ...) {
365   xine_stream_private_t *stream = (xine_stream_private_t *)s;
366 
367   stream = stream->side_streams[0];
368   if (meta_valid (stream, info)) {
369     va_list   ap;
370     char     *args[1025];
371     char     *buf;
372     size_t    n, len;
373 
374     len = n = 0;
375 
376     va_start(ap, info);
377     while((buf = va_arg(ap, char *)) && (n < 1024)) {
378       len += strlen(buf) + 1;
379       args[n] = buf;
380       n++;
381     }
382     va_end(ap);
383 
384     args[n] = NULL;
385 
386     if(len) {
387       char *p, *meta;
388 
389       p = meta = (char *) malloc(len + 1);
390 
391       n = 0;
392       while(args[n]) {
393 	strcpy(meta, args[n]);
394 	meta += strlen(args[n]) + 1;
395 	n++;
396       }
397 
398       *meta = '\0';
399 
400       xine_rwlock_wrlock (&stream->meta_lock);
401       if (stream->meta_info_public[info] != stream->meta_info[info])
402         free (stream->meta_info[info]);
403       stream->meta_info[info] = p;
404       if (p)
405         meta_info_chomp (p);
406       xine_rwlock_unlock (&stream->meta_lock);
407     }
408   }
409 }
410 
411 /*
412  * Retrieve private info value.
413  */
_x_meta_info_get(xine_stream_t * s,int info)414 const char *_x_meta_info_get (xine_stream_t *s, int info) {
415   xine_stream_private_t *stream = (xine_stream_private_t *)s;
416   const char *meta_info;
417   stream = stream->side_streams[0];
418   xine_rwlock_rdlock (&stream->meta_lock);
419   meta_info = stream->meta_info[info];
420   xine_rwlock_unlock (&stream->meta_lock);
421   return meta_info;
422 }
423 
424 /*
425  * Retrieve public info value.
426  */
_x_meta_info_get_public(xine_stream_t * s,int info)427 const char *_x_meta_info_get_public (xine_stream_t *s, int info) {
428   xine_stream_private_t *stream = (xine_stream_private_t *)s;
429   char *pub_meta_info = NULL;
430   stream = stream->side_streams[0];
431   if (meta_valid (stream, info)) {
432     xine_rwlock_rdlock (&stream->meta_lock);
433     pub_meta_info = stream->meta_info_public[info];
434     if (pub_meta_info != stream->meta_info[info]) {
435       xine_rwlock_unlock (&stream->meta_lock);
436       xine_rwlock_wrlock (&stream->meta_lock);
437       free (pub_meta_info);
438       stream->meta_info_public[info] = pub_meta_info = stream->meta_info[info];
439     }
440     xine_rwlock_unlock (&stream->meta_lock);
441   }
442   return pub_meta_info;
443 }
444 
445