]> git.sesse.net Git - vlc/blob - src/config/chain.c
vlc security: As i've seen very little improvement on that front, i've decided to...
[vlc] / src / config / chain.c
1 /*****************************************************************************
2  * chain.c : configuration module chain parsing stuff
3  *****************************************************************************
4  * Copyright (C) 2002-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Eric Petit <titer@videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #include <vlc/vlc.h>
31 #include "libvlc.h"
32
33 #include "vlc_interface.h"
34
35 /*****************************************************************************
36  * Local prototypes
37  *****************************************************************************/
38
39 /* chain format:
40     module{option=*:option=*}[:module{option=*:...}]
41  */
42 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
43 #define SKIPTRAILINGSPACE( p, e ) \
44     { while( e > p && ( *(e-1) == ' ' || *(e-1) == '\t' ) ) e--; }
45
46 /* go accross " " and { } */
47 static const char *_get_chain_end( const char *str )
48 {
49     char c;
50     const char *p = str;
51
52     SKIPSPACE( p );
53
54     for( ;; )
55     {
56         if( !*p || *p == ',' || *p == '}' ) return p;
57
58         if( *p != '{' && *p != '"' && *p != '\'' )
59         {
60             p++;
61             continue;
62         }
63
64         if( *p == '{' ) c = '}';
65         else c = *p;
66         p++;
67
68         for( ;; )
69         {
70             if( !*p ) return p;
71
72             if( *p == c ) return ++p;
73             else if( *p == '{' && c == '}' ) p = _get_chain_end( p );
74             else p++;
75         }
76     }
77 }
78
79 char *config_ChainCreate( char **ppsz_name, config_chain_t **pp_cfg, const char *psz_chain )
80 {
81     config_chain_t *p_cfg = NULL;
82     const char *p = psz_chain;
83
84     *ppsz_name = NULL;
85     *pp_cfg    = NULL;
86
87     if( !p ) return NULL;
88
89     SKIPSPACE( p );
90
91     while( *p && *p != '{' && *p != ':' && *p != ' ' && *p != '\t' ) p++;
92
93     if( p == psz_chain ) return NULL;
94
95     *ppsz_name = strndup( psz_chain, p - psz_chain );
96
97     SKIPSPACE( p );
98
99     if( *p == '{' )
100     {
101         const char *psz_name;
102
103         p++;
104
105         for( ;; )
106         {
107             config_chain_t cfg;
108
109             SKIPSPACE( p );
110
111             psz_name = p;
112
113             while( *p && *p != '=' && *p != ',' && *p != '{' && *p != '}' &&
114                    *p != ' ' && *p != '\t' ) p++;
115
116             /* fprintf( stderr, "name=%s - rest=%s\n", psz_name, p ); */
117             if( p == psz_name )
118             {
119                 fprintf( stderr, "invalid options (empty)" );
120                 break;
121             }
122
123             cfg.psz_name = strndup( psz_name, p - psz_name );
124
125             SKIPSPACE( p );
126
127             if( *p == '=' || *p == '{' )
128             {
129                 const char *end;
130                 vlc_bool_t b_keep_brackets = (*p == '{');
131
132                 if( *p == '=' ) p++;
133
134                 end = _get_chain_end( p );
135                 if( end <= p )
136                 {
137                     cfg.psz_value = NULL;
138                 }
139                 else
140                 {
141                     /* Skip heading and trailing spaces.
142                      * This ain't necessary but will avoid simple
143                      * user mistakes. */
144                     SKIPSPACE( p );
145                 }
146
147                 if( end <= p )
148                 {
149                     cfg.psz_value = NULL;
150                 }
151                 else
152                 {
153                     if( *p == '\'' || *p == '"' ||
154                         ( !b_keep_brackets && *p == '{' ) )
155                     {
156                         p++;
157
158                         if( *(end-1) != '\'' && *(end-1) == '"' )
159                             SKIPTRAILINGSPACE( p, end );
160
161                         if( end - 1 <= p ) cfg.psz_value = NULL;
162                         else cfg.psz_value = strndup( p, end -1 - p );
163                     }
164                     else
165                     {
166                         SKIPTRAILINGSPACE( p, end );
167                         if( end <= p ) cfg.psz_value = NULL;
168                         else cfg.psz_value = strndup( p, end - p );
169                     }
170                 }
171
172                 p = end;
173                 SKIPSPACE( p );
174             }
175             else
176             {
177                 cfg.psz_value = NULL;
178             }
179
180             cfg.p_next = NULL;
181             if( p_cfg )
182             {
183                 p_cfg->p_next = malloc( sizeof( config_chain_t ) );
184                 memcpy( p_cfg->p_next, &cfg, sizeof( config_chain_t ) );
185
186                 p_cfg = p_cfg->p_next;
187             }
188             else
189             {
190                 p_cfg = malloc( sizeof( config_chain_t ) );
191                 memcpy( p_cfg, &cfg, sizeof( config_chain_t ) );
192
193                 *pp_cfg = p_cfg;
194             }
195
196             if( *p == ',' ) p++;
197
198             if( *p == '}' )
199             {
200                 p++;
201                 break;
202             }
203         }
204     }
205
206     if( *p == ':' ) return( strdup( p + 1 ) );
207
208     return NULL;
209 }
210
211 void config_ChainDestroy( config_chain_t *p_cfg )
212 {
213     while( p_cfg != NULL )
214     {
215         config_chain_t *p_next;
216
217         p_next = p_cfg->p_next;
218
219         FREENULL( p_cfg->psz_name );
220         FREENULL( p_cfg->psz_value );
221         free( p_cfg );
222
223         p_cfg = p_next;
224     }
225 }
226
227 void __config_ChainParse( vlc_object_t *p_this, const char *psz_prefix,
228                           const char *const *ppsz_options, config_chain_t *cfg )
229 {
230     if( psz_prefix == NULL ) psz_prefix = "";
231     size_t plen = 1 + strlen( psz_prefix );
232
233     /* First, var_Create all variables */
234     for( size_t i = 0; ppsz_options[i] != NULL; i++ )
235     {
236         const char *optname = ppsz_options[i];
237         if (optname[0] == '*')
238             optname++;
239
240         char name[plen + strlen( optname )];
241         snprintf( name, sizeof (name), "%s%s", psz_prefix, optname );
242         if( var_Create( p_this, name,
243                         config_GetType( p_this, name ) | VLC_VAR_DOINHERIT ) )
244             return /* VLC_xxx */;
245     }
246
247     /* Now parse options and set value */
248     for(; cfg; cfg = cfg->p_next )
249     {
250         vlc_value_t val;
251         vlc_bool_t b_yes = VLC_TRUE;
252         vlc_bool_t b_once = VLC_FALSE;
253         module_config_t *p_conf;
254         int i_type;
255         size_t i;
256
257         if( cfg->psz_name == NULL || *cfg->psz_name == '\0' )
258             continue;
259
260         for( i = 0; ppsz_options[i] != NULL; i++ )
261         {
262             if( !strcmp( ppsz_options[i], cfg->psz_name ) )
263             {
264                 break;
265             }
266             if( ( !strncmp( cfg->psz_name, "no-", 3 ) &&
267                   !strcmp( ppsz_options[i], cfg->psz_name + 3 ) ) ||
268                 ( !strncmp( cfg->psz_name, "no", 2 ) &&
269                   !strcmp( ppsz_options[i], cfg->psz_name + 2 ) ) )
270             {
271                 b_yes = VLC_FALSE;
272                 break;
273             }
274
275             if( *ppsz_options[i] == '*' &&
276                 !strcmp( &ppsz_options[i][1], cfg->psz_name ) )
277             {
278                 b_once = VLC_TRUE;
279                 break;
280             }
281
282         }
283
284         if( ppsz_options[i] == NULL )
285         {
286             msg_Warn( p_this, "option %s is unknown", cfg->psz_name );
287             continue;
288         }
289
290         /* create name */
291         char name[plen + strlen( ppsz_options[i] )];
292         const char *psz_name = name;
293         snprintf( name, sizeof (name), "%s%s", psz_prefix,
294                   b_once ? (ppsz_options[i] + 1) : ppsz_options[i] );
295
296         /* Check if the option is deprecated */
297         p_conf = config_FindConfig( p_this, name );
298
299         /* This is basically cut and paste from src/misc/configuration.c
300          * with slight changes */
301         if( p_conf )
302         {
303             if( p_conf->b_removed )
304             {
305                 msg_Err( p_this, "Option %s is not supported anymore.",
306                          name );
307                 /* TODO: this should return an error and end option parsing
308                  * ... but doing this would change the VLC API and all the
309                  * modules so i'll do it later */
310                 continue;
311             }
312             if( p_conf->psz_oldname
313              && !strcmp( p_conf->psz_oldname, name ) )
314             {
315                  psz_name = p_conf->psz_name;
316                  msg_Warn( p_this, "Option %s is obsolete. Use %s instead.",
317                            name, psz_name );
318             }
319             if( p_conf->b_unsafe )
320             {
321                 int policy = config_GetInt( p_this, "security-policy" );
322                 switch( policy )
323                 {
324                     case 0: /* block */
325                         msg_Err( p_this, "option %s is unsafe and is blocked by security policy", psz_name );
326                         return;
327                     case 1: /* allow */
328                         break;
329                     case 2: /* prompt */
330                     {
331                         char description[256];
332                         snprintf(description, sizeof(description), _("playlist item is making use of the following unsafe option '%s', which may be harmful if used in a malicious way, authorize it ?"), psz_name);
333                         if( DIALOG_OK_YES != intf_UserYesNo( p_this, _("WARNING: Unsafe Playlist"), description, _("Yes"), _("No"), NULL) )
334                         {
335                             msg_Err( p_this, "option %s is unsafe and is blocked by security policy", psz_name );
336                             return;
337                         }
338                     }
339                     default:
340                         ;
341                 }
342             }
343         }
344         /* </Check if the option is deprecated> */
345
346         /* get the type of the variable */
347         i_type = config_GetType( p_this, psz_name );
348         if( !i_type )
349         {
350             msg_Warn( p_this, "unknown option %s (value=%s)",
351                       cfg->psz_name, cfg->psz_value );
352             continue;
353         }
354
355         i_type &= CONFIG_ITEM;
356
357         if( i_type != VLC_VAR_BOOL && cfg->psz_value == NULL )
358         {
359             msg_Warn( p_this, "missing value for option %s", cfg->psz_name );
360             continue;
361         }
362         if( i_type != VLC_VAR_STRING && b_once )
363         {
364             msg_Warn( p_this, "*option_name need to be a string option" );
365             continue;
366         }
367
368         switch( i_type )
369         {
370             case VLC_VAR_BOOL:
371                 val.b_bool = b_yes;
372                 break;
373             case VLC_VAR_INTEGER:
374                 val.i_int = strtol( cfg->psz_value ? cfg->psz_value : "0",
375                                     NULL, 0 );
376                 break;
377             case VLC_VAR_FLOAT:
378                 val.f_float = atof( cfg->psz_value ? cfg->psz_value : "0" );
379                 break;
380             case VLC_VAR_STRING:
381             case VLC_VAR_MODULE:
382                 val.psz_string = cfg->psz_value;
383                 break;
384             default:
385                 msg_Warn( p_this, "unhandled config var type (%d)", i_type );
386                 memset( &val, 0, sizeof( vlc_value_t ) );
387                 break;
388         }
389         if( b_once )
390         {
391             vlc_value_t val2;
392
393             var_Get( p_this, psz_name, &val2 );
394             if( *val2.psz_string )
395             {
396                 free( val2.psz_string );
397                 msg_Dbg( p_this, "ignoring option %s (not first occurrence)", psz_name );
398                 continue;
399             }
400             free( val2.psz_string );
401         }
402         var_Set( p_this, psz_name, val );
403         msg_Dbg( p_this, "set config option: %s to %s", psz_name,
404                  cfg->psz_value ? cfg->psz_value : "(null)" );
405     }
406 }