]> git.sesse.net Git - vlc/blob - modules/control/http/util.c
utf8_* -> vlc_* (sed roxxors)
[vlc] / modules / control / http / util.c
1 /*****************************************************************************
2  * util.c : Utility functions for HTTP interface
3  *****************************************************************************
4  * Copyright (C) 2001-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Christophe Massiot <massiot@via.ecp.fr>
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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include "http.h"
32 #include <vlc_strings.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <sys/stat.h>
36 #include <vlc_fs.h>
37
38 /****************************************************************************
39  * File and directory functions
40  ****************************************************************************/
41
42 /* ToUrl: create a good name for an url from filename */
43 char *FileToUrl( char *name, bool *pb_index )
44 {
45     char *url, *p;
46
47     url = p = malloc( strlen( name ) + 1 );
48
49     *pb_index = false;
50     if( !url || !p )
51     {
52         return NULL;
53     }
54
55 #ifdef WIN32
56     while( *name == '\\' || *name == '/' )
57 #else
58     while( *name == '/' )
59 #endif
60     {
61         name++;
62     }
63
64     *p++ = '/';
65     strcpy( p, name );
66
67 #ifdef WIN32
68     /* convert '\\' into '/' */
69     name = p;
70     while( *name )
71     {
72         if( *name == '\\' )
73             *name = '/';
74         name++;
75     }
76 #endif
77
78     /* index.* -> / */
79     if( ( p = strrchr( url, '/' ) ) != NULL )
80     {
81         if( !strncmp( p, "/index.", 7 ) )
82         {
83             p[1] = '\0';
84             *pb_index = true;
85         }
86     }
87     return url;
88 }
89
90 /* Load a file */
91 int FileLoad( FILE *f, char **pp_data, int *pi_data )
92 {
93     int i_read;
94
95     /* just load the file */
96     *pi_data = 0;
97     *pp_data = xmalloc( 1025 );  /* +1 for \0 */
98
99     while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
100     {
101         *pi_data += 1024;
102         *pp_data = xrealloc( *pp_data, *pi_data  + 1025 );
103     }
104     if( i_read > 0 )
105     {
106         *pi_data += i_read;
107     }
108     (*pp_data)[*pi_data] = '\0';
109
110     return VLC_SUCCESS;
111 }
112
113 /* Parse a directory and recursively add files */
114 int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
115                         char *psz_dir )
116 {
117     intf_sys_t     *p_sys = p_intf->p_sys;
118     char           dir[MAX_DIR_SIZE];
119     DIR           *p_dir;
120     vlc_acl_t     *p_acl;
121     FILE          *file;
122
123     char          *user = NULL;
124     char          *password = NULL;
125
126     int           i_dirlen;
127
128     if( ( p_dir = vlc_opendir( psz_dir ) ) == NULL )
129     {
130         if( errno != ENOENT && errno != ENOTDIR )
131             msg_Err( p_intf, "cannot open directory (%s)", psz_dir );
132         return VLC_EGENERIC;
133     }
134
135     i_dirlen = strlen( psz_dir );
136     if( i_dirlen + 10 > MAX_DIR_SIZE )
137     {
138         msg_Warn( p_intf, "skipping too deep directory (%s)", psz_dir );
139         closedir( p_dir );
140         return 0;
141     }
142
143     msg_Dbg( p_intf, "dir=%s", psz_dir );
144
145     snprintf( dir, sizeof( dir ), "%s"DIR_SEP".access", psz_dir );
146     if( ( file = vlc_fopen( dir, "r" ) ) != NULL )
147     {
148         char line[1024];
149         int  i_size;
150
151         msg_Dbg( p_intf, "find .access in dir=%s", psz_dir );
152
153         i_size = fread( line, 1, 1023, file );
154         if( i_size > 0 )
155         {
156             char *p;
157             while( i_size > 0 && ( line[i_size-1] == '\n' ||
158                    line[i_size-1] == '\r' ) )
159             {
160                 i_size--;
161             }
162
163             line[i_size] = '\0';
164
165             p = strchr( line, ':' );
166             if( p )
167             {
168                 *p++ = '\0';
169                 user = strdup( line );
170                 password = strdup( p );
171             }
172         }
173         msg_Dbg( p_intf, "using user=%s (read=%d)", user, i_size );
174
175         fclose( file );
176     }
177
178     snprintf( dir, sizeof( dir ), "%s"DIR_SEP".hosts", psz_dir );
179     p_acl = ACL_Create( p_intf, false );
180     if( ACL_LoadFile( p_acl, dir ) )
181     {
182         ACL_Destroy( p_acl );
183
184         struct stat st;
185         if( vlc_stat( dir, &st ) == 0 )
186         {
187             free( user );
188             free( password );
189             closedir( p_dir );
190             return VLC_EGENERIC;
191         }
192         p_acl = NULL;
193     }
194
195     for( ;; )
196     {
197         char *psz_filename;
198         /* parse psz_src dir */
199         if( ( psz_filename = vlc_readdir( p_dir ) ) == NULL )
200         {
201             break;
202         }
203
204         if( ( psz_filename[0] == '.' )
205          || ( i_dirlen + strlen( psz_filename ) > MAX_DIR_SIZE ) )
206         {
207             free( psz_filename );
208             continue;
209         }
210
211         snprintf( dir, sizeof( dir ), "%s"DIR_SEP"%s", psz_dir, psz_filename );
212         free( psz_filename );
213
214         if( ParseDirectory( p_intf, psz_root, dir ) )
215         {
216             httpd_file_sys_t *f = NULL;
217             httpd_handler_sys_t *h = NULL;
218             bool b_index;
219             char *psz_name, *psz_ext;
220
221             psz_name = FileToUrl( &dir[strlen( psz_root )], &b_index );
222             psz_ext = strrchr( dir, '.' );
223             if( psz_ext != NULL )
224             {
225                 int i;
226                 psz_ext++;
227                 for( i = 0; i < p_sys->i_handlers; i++ )
228                     if( !strcmp( p_sys->pp_handlers[i]->psz_ext, psz_ext ) )
229                         break;
230                 if( i < p_sys->i_handlers )
231                 {
232                     f = malloc( sizeof( httpd_handler_sys_t ) );
233                     h = (httpd_handler_sys_t *)f;
234                     f->b_handler = true;
235                     h->p_association = p_sys->pp_handlers[i];
236                 }
237             }
238             if( f == NULL )
239             {
240                 f = xmalloc( sizeof( httpd_file_sys_t ) );
241                 f->b_handler = false;
242             }
243
244             f->p_intf  = p_intf;
245             f->p_file = NULL;
246             f->p_redir = NULL;
247             f->p_redir2 = NULL;
248             f->file = strdup (dir);
249             f->name = psz_name;
250             f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) || strstr( &dir[strlen( psz_root )], ".xml" ) ? true : false;
251
252             if( !f->name )
253             {
254                 msg_Err( p_intf , "unable to parse directory" );
255                 closedir( p_dir );
256                 free( f );
257                 return( VLC_ENOMEM );
258             }
259             msg_Dbg( p_intf, "file=%s (url=%s)",
260                      f->file, f->name );
261
262             if( !f->b_handler )
263             {
264                 char *psz_type = strdup( "text/html; charset=UTF-8" );
265                 if( strstr( &dir[strlen( psz_root )], ".xml" ) )
266                 {
267                     char *psz = strstr( psz_type, "html;" );
268                     if( psz )
269                     {
270                         psz[0] = 'x';
271                         psz[1] = 'm';
272                         psz[2] = 'l';
273                         psz[3] = ';';
274                         psz[4] = ' ';
275                     }
276                 }
277                 f->p_file = httpd_FileNew( p_sys->p_httpd_host,
278                                            f->name,
279                                            f->b_html ? psz_type : NULL,
280                                            user, password, p_acl,
281                                            HttpCallback, f );
282                 free( psz_type );
283                 if( f->p_file != NULL )
284                 {
285                     TAB_APPEND( p_sys->i_files, p_sys->pp_files, f );
286                 }
287             }
288             else
289             {
290                 h->p_handler = httpd_HandlerNew( p_sys->p_httpd_host,
291                                                  f->name,
292                                                  user, password, p_acl,
293                                                  HandlerCallback, h );
294                 if( h->p_handler != NULL )
295                 {
296                     TAB_APPEND( p_sys->i_files, p_sys->pp_files,
297                                 (httpd_file_sys_t *)h );
298                 }
299             }
300
301             /* for url that ends by / add
302              *  - a redirect from rep to rep/
303              *  - in case of index.* rep/index.html to rep/ */
304             if( f && f->name[strlen(f->name) - 1] == '/' )
305             {
306                 char *psz_redir = strdup( f->name );
307                 char *p;
308                 psz_redir[strlen( psz_redir ) - 1] = '\0';
309
310                 msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
311                 f->p_redir = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir );
312                 free( psz_redir );
313
314                 if( b_index && ( p = strstr( f->file, "index." ) ) )
315                 {
316                     if( asprintf( &psz_redir, "%s%s", f->name, p ) != -1 )
317                     {
318                         msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
319                         f->p_redir2 = httpd_RedirectNew( p_sys->p_httpd_host,
320                                                          f->name, psz_redir );
321
322                         free( psz_redir );
323                     }
324                 }
325             }
326         }
327     }
328
329     free( user );
330     free( password );
331
332     ACL_Destroy( p_acl );
333     closedir( p_dir );
334
335     return VLC_SUCCESS;
336 }
337
338
339 /*************************************************************************
340  * Playlist stuff
341  *************************************************************************/
342 void PlaylistListNode( intf_thread_t *p_intf, playlist_t *p_pl,
343                            playlist_item_t *p_node, char *name, mvar_t *s,
344                            int i_depth )
345 {
346     if( !p_node || !p_node->p_input )
347         return;
348
349     if( p_node->i_children == -1 )
350     {
351         char value[512];
352         char *psz;
353         playlist_item_t * p_item = playlist_CurrentPlayingItem( p_pl );
354         if( !p_item || !p_item->p_input )
355             return;
356
357         mvar_t *itm = mvar_New( name, "set" );
358         if( p_item->p_input == p_node->p_input )
359             mvar_AppendNewVar( itm, "current", "1" );
360         else
361             mvar_AppendNewVar( itm, "current", "0" );
362
363         sprintf( value, "%d", p_node->i_id );
364         mvar_AppendNewVar( itm, "index", value );
365
366         psz = input_item_GetName( p_node->p_input );
367         mvar_AppendNewVar( itm, "name", psz );
368         free( psz );
369
370         psz = input_item_GetURI( p_node->p_input );
371         mvar_AppendNewVar( itm, "uri", psz );
372         free( psz );
373
374         mvar_AppendNewVar( itm, "type", "Item" );
375
376         sprintf( value, "%d", i_depth );
377         mvar_AppendNewVar( itm, "depth", value );
378
379         if( p_node->i_flags & PLAYLIST_RO_FLAG )
380             mvar_AppendNewVar( itm, "ro", "ro" );
381         else
382             mvar_AppendNewVar( itm, "ro", "rw" );
383
384         sprintf( value, "%"PRId64, input_item_GetDuration( p_node->p_input ) );
385         mvar_AppendNewVar( itm, "duration", value );
386
387         //Adding extra meta-information to each playlist item
388
389         psz = input_item_GetTitle( p_node->p_input );
390         mvar_AppendNewVar( itm, "title", psz );
391         free( psz );
392
393         psz = input_item_GetArtist( p_node->p_input );
394         mvar_AppendNewVar( itm, "artist", psz );
395         free( psz );
396
397         psz = input_item_GetGenre( p_node->p_input );
398         mvar_AppendNewVar( itm, "genre", psz );
399         free( psz );
400
401         psz = input_item_GetCopyright( p_node->p_input );
402         mvar_AppendNewVar( itm, "copyright", psz );
403         free( psz );
404
405         psz = input_item_GetAlbum( p_node->p_input );
406         mvar_AppendNewVar( itm, "album", psz );
407         free( psz );
408
409         psz = input_item_GetTrackNum( p_node->p_input );
410         mvar_AppendNewVar( itm, "track", psz );
411         free( psz );
412
413         psz = input_item_GetDescription( p_node->p_input );
414         mvar_AppendNewVar( itm, "description", psz );
415         free( psz );
416
417         psz = input_item_GetRating( p_node->p_input );
418         mvar_AppendNewVar( itm, "rating", psz );
419         free( psz );
420
421         psz = input_item_GetDate( p_node->p_input );
422         mvar_AppendNewVar( itm, "date", psz );
423         free( psz );
424
425         psz = input_item_GetURL( p_node->p_input );
426         mvar_AppendNewVar( itm, "url", psz );
427         free( psz );
428
429         psz = input_item_GetLanguage( p_node->p_input );
430         mvar_AppendNewVar( itm, "language", psz );
431         free( psz );
432
433         psz = input_item_GetNowPlaying( p_node->p_input );
434         mvar_AppendNewVar( itm, "now_playing", psz );
435         free( psz );
436
437         psz = input_item_GetPublisher( p_node->p_input );
438         mvar_AppendNewVar( itm, "publisher", psz );
439         free( psz );
440
441         psz = input_item_GetEncodedBy( p_node->p_input );
442         mvar_AppendNewVar( itm, "encoded_by", psz );
443         free( psz );
444
445         psz = input_item_GetArtURL( p_node->p_input );
446         mvar_AppendNewVar( itm, "art_url", psz );
447         free( psz );
448
449         psz = input_item_GetTrackID( p_node->p_input );
450         mvar_AppendNewVar( itm, "track_id", psz );
451         free( psz );
452
453         mvar_AppendVar( s, itm );
454     }
455     else
456     {
457         char value[512];
458         int i_child;
459         mvar_t *itm = mvar_New( name, "set" );
460
461         mvar_AppendNewVar( itm, "name", p_node->p_input->psz_name );
462         mvar_AppendNewVar( itm, "uri", p_node->p_input->psz_name );
463
464         mvar_AppendNewVar( itm, "type", "Node" );
465
466         sprintf( value, "%d", p_node->i_id );
467         mvar_AppendNewVar( itm, "index", value );
468
469         sprintf( value, "%d", p_node->i_children);
470         mvar_AppendNewVar( itm, "i_children", value );
471
472         sprintf( value, "%d", i_depth );
473         mvar_AppendNewVar( itm, "depth", value );
474
475         if( p_node->i_flags & PLAYLIST_RO_FLAG )
476             mvar_AppendNewVar( itm, "ro", "ro" );
477         else
478             mvar_AppendNewVar( itm, "ro", "rw" );
479
480         mvar_AppendVar( s, itm );
481
482         for( i_child = 0 ; i_child < p_node->i_children ; i_child++ )
483              PlaylistListNode( p_intf, p_pl, p_node->pp_children[i_child],
484                                name, s, i_depth + 1);
485     }
486 }
487
488 /****************************************************************************
489  * Seek command parsing handling
490  ****************************************************************************/
491 void HandleSeek( intf_thread_t *p_intf, char *p_value )
492 {
493     intf_sys_t     *p_sys = p_intf->p_sys;
494     vlc_value_t val;
495     int i_stock = 0;
496     uint64_t i_length;
497     int i_value = 0;
498     int i_relative = 0;
499 #define POSITION_ABSOLUTE 12
500 #define POSITION_REL_FOR 13
501 #define POSITION_REL_BACK 11
502 #define VL_TIME_ABSOLUTE 0
503 #define VL_TIME_REL_FOR 1
504 #define VL_TIME_REL_BACK -1
505     if( p_sys->p_input )
506     {
507         var_Get( p_sys->p_input, "length", &val );
508         i_length = val.i_time;
509
510         while( p_value[0] != '\0' )
511         {
512             switch(p_value[0])
513             {
514                 case '+':
515                 {
516                     i_relative = VL_TIME_REL_FOR;
517                     p_value++;
518                     break;
519                 }
520                 case '-':
521                 {
522                     i_relative = VL_TIME_REL_BACK;
523                     p_value++;
524                     break;
525                 }
526                 case '0': case '1': case '2': case '3': case '4':
527                 case '5': case '6': case '7': case '8': case '9':
528                 {
529                     i_stock = strtol( p_value , &p_value , 10 );
530                     break;
531                 }
532                 case '%': /* for percentage ie position */
533                 {
534                     i_relative += POSITION_ABSOLUTE;
535                     i_value = i_stock;
536                     i_stock = 0;
537                     p_value[0] = '\0';
538                     break;
539                 }
540                 case ':':
541                 {
542                     i_value = 60 * (i_value + i_stock) ;
543                     i_stock = 0;
544                     p_value++;
545                     break;
546                 }
547                 case 'h': case 'H': /* hours */
548                 {
549                     i_value += 3600 * i_stock;
550                     i_stock = 0;
551                     /* other characters which are not numbers are not
552                      * important */
553                     while( ((p_value[0] < '0') || (p_value[0] > '9'))
554                            && (p_value[0] != '\0') )
555                     {
556                         p_value++;
557                     }
558                     break;
559                 }
560                 case 'm': case 'M': case '\'': /* minutes */
561                 {
562                     i_value += 60 * i_stock;
563                     i_stock = 0;
564                     p_value++;
565                     while( ((p_value[0] < '0') || (p_value[0] > '9'))
566                            && (p_value[0] != '\0') )
567                     {
568                         p_value++;
569                     }
570                     break;
571                 }
572                 case 's': case 'S': case '"':  /* seconds */
573                 {
574                     i_value += i_stock;
575                     i_stock = 0;
576                     while( ((p_value[0] < '0') || (p_value[0] > '9'))
577                            && (p_value[0] != '\0') )
578                     {
579                         p_value++;
580                     }
581                     break;
582                 }
583                 default:
584                 {
585                     p_value++;
586                     break;
587                 }
588             }
589         }
590
591         /* if there is no known symbol, I consider it as seconds.
592          * Otherwise, i_stock = 0 */
593         i_value += i_stock;
594
595         switch(i_relative)
596         {
597             case VL_TIME_ABSOLUTE:
598             {
599                 if( (uint64_t)( i_value ) * 1000000 <= i_length )
600                     val.i_time = (uint64_t)( i_value ) * 1000000;
601                 else
602                     val.i_time = i_length;
603
604                 var_Set( p_sys->p_input, "time", val );
605                 msg_Dbg( p_intf, "requested seek position: %dsec", i_value );
606                 break;
607             }
608             case VL_TIME_REL_FOR:
609             {
610                 var_Get( p_sys->p_input, "time", &val );
611                 if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length )
612                 {
613                     val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time;
614                 } else
615                 {
616                     val.i_time = i_length;
617                 }
618                 var_Set( p_sys->p_input, "time", val );
619                 msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value );
620                 break;
621             }
622             case VL_TIME_REL_BACK:
623             {
624                 var_Get( p_sys->p_input, "time", &val );
625                 if( (int64_t)( i_value ) * 1000000 > val.i_time )
626                 {
627                     val.i_time = 0;
628                 } else
629                 {
630                     val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000);
631                 }
632                 var_Set( p_sys->p_input, "time", val );
633                 msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value );
634                 break;
635             }
636             case POSITION_ABSOLUTE:
637             {
638                 val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 ,
639                                             0.0 ), 100.0 );
640                 var_Set( p_sys->p_input, "position", val );
641                 msg_Dbg( p_intf, "requested seek percent: %d%%", i_value );
642                 break;
643             }
644             case POSITION_REL_FOR:
645             {
646                 var_Get( p_sys->p_input, "position", &val );
647                 val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0,
648                                              0.0 ) , 100.0 );
649                 var_Set( p_sys->p_input, "position", val );
650                 msg_Dbg( p_intf, "requested seek percent forward: %d%%",
651                          i_value );
652                 break;
653             }
654             case POSITION_REL_BACK:
655             {
656                 var_Get( p_sys->p_input, "position", &val );
657                 val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0,
658                                              0.0 ) , 100.0 );
659                 var_Set( p_sys->p_input, "position", val );
660                 msg_Dbg( p_intf, "requested seek percent backward: %d%%",
661                          i_value );
662                 break;
663             }
664             default:
665             {
666                 msg_Dbg( p_intf, "invalid seek request" );
667                 break;
668             }
669         }
670     }
671 #undef POSITION_ABSOLUTE
672 #undef POSITION_REL_FOR
673 #undef POSITION_REL_BACK
674 #undef VL_TIME_ABSOLUTE
675 #undef VL_TIME_REL_FOR
676 #undef VL_TIME_REL_BACK
677 }
678
679
680 /****************************************************************************
681  * URI Parsing functions
682  ****************************************************************************/
683 int TestURIParam( char *psz_uri, const char *psz_name )
684 {
685     char *p = psz_uri;
686
687     while( (p = strstr( p, psz_name )) )
688     {
689         /* Verify that we are dealing with a post/get argument */
690         if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
691               && p[strlen(psz_name)] == '=' )
692         {
693             return true;
694         }
695         p++;
696     }
697
698     return false;
699 }
700
701 static const char *FindURIValue( const char *psz_uri, const char *restrict psz_name,
702                            size_t *restrict p_len )
703 {
704     const char *p = psz_uri, *end;
705     size_t len;
706
707     while( (p = strstr( p, psz_name )) )
708     {
709         /* Verify that we are dealing with a post/get argument */
710         if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
711               && p[strlen(psz_name)] == '=' )
712             break;
713         p++;
714     }
715
716     if( p == NULL )
717     {
718         *p_len = 0;
719         return NULL;
720     }
721
722     p += strlen( psz_name );
723     if( *p == '=' ) p++;
724
725     if( ( end = strchr( p, '\n' ) ) != NULL )
726     {
727         /* POST method */
728         if( ( end > p ) && ( end[-1] == '\r' ) )
729             end--;
730
731         len = end - p;
732     }
733     else
734     {
735         /* GET method */
736         if( ( end = strchr( p, '&' ) ) != NULL )
737             len = end - p;
738         else
739             len = strlen( p );
740     }
741
742     *p_len = len;
743     return p;
744 }
745
746 const char *ExtractURIValue( const char *restrict psz_uri,
747                            const char *restrict psz_name,
748                            char *restrict psz_buf, size_t bufsize )
749 {
750     size_t len;
751     const char *psz_value = FindURIValue( psz_uri, psz_name, &len );
752     const char *psz_next;
753
754     if( psz_value == NULL )
755     {
756         if( bufsize > 0 )
757             *psz_buf = '\0';
758         return NULL;
759     }
760
761     psz_next = psz_value + len;
762
763     if( len >= bufsize )
764         len = bufsize - 1;
765
766     if( len > 0 )
767         strncpy( psz_buf, psz_value, len );
768     if( bufsize > 0 )
769         psz_buf[len] = '\0';
770
771     return psz_next;
772 }
773
774 char *ExtractURIString( const char *restrict psz_uri,
775                             const char *restrict psz_name )
776 {
777     size_t len;
778     const char *psz_value = FindURIValue( psz_uri, psz_name, &len );
779
780     if( psz_value == NULL )
781         return NULL;
782
783     char *res = malloc( len + 1 );
784     if( res == NULL )
785         return NULL;
786
787     memcpy( res, psz_value, len );
788     res[len] = '\0';
789
790     return res;
791 }
792
793 /* Since the resulting string is smaller we can work in place, so it is
794  * permitted to have psz == new. new points to the first word of the
795  * string, the function returns the remaining string. */
796 char *FirstWord( char *psz, char *new )
797 {
798     bool b_end;
799
800     while( *psz == ' ' )
801         psz++;
802
803     while( *psz != '\0' && *psz != ' ' )
804     {
805         if( *psz == '\'' )
806         {
807             char c = *psz++;
808             while( *psz != '\0' && *psz != c )
809             {
810                 if( *psz == '\\' && psz[1] != '\0' )
811                     psz++;
812                 *new++ = *psz++;
813             }
814             if( *psz == c )
815                 psz++;
816         }
817         else
818         {
819             if( *psz == '\\' && psz[1] != '\0' )
820                 psz++;
821             *new++ = *psz++;
822         }
823     }
824     b_end = !*psz;
825
826     *new++ = '\0';
827     if( !b_end )
828         return psz + 1;
829     else
830         return NULL;
831 }
832
833 /**********************************************************************
834  * MRLParse: parse the MRL, find the MRL string and the options,
835  * create an item with all information in it, and return the item.
836  * return NULL if there is an error.
837  **********************************************************************/
838
839 /* Function analog to FirstWord except that it relies on colon instead
840  * of space to delimit option boundaries. */
841 static char *FirstOption( char *psz, char *new )
842 {
843     bool b_end, b_start = true;
844
845     while( *psz == ' ' )
846         psz++;
847
848     while( *psz != '\0' && (*psz != ' ' || psz[1] != ':') )
849     {
850         if( *psz == '\'' )
851         {
852             char c = *psz++;
853             while( *psz != '\0' && *psz != c )
854             {
855                 if( *psz == '\\' && psz[1] != '\0' )
856                     psz++;
857                 *new++ = *psz++;
858                 b_start = false;
859             }
860             if( *psz == c )
861                 psz++;
862         }
863         else
864         {
865             if( *psz == '\\' && psz[1] != '\0' )
866                 psz++;
867             *new++ = *psz++;
868             b_start = false;
869         }
870     }
871     b_end = !*psz;
872
873     if ( !b_start )
874         while (new[-1] == ' ')
875             new--;
876
877     *new++ = '\0';
878     if( !b_end )
879         return psz + 1;
880     else
881         return NULL;
882 }
883
884 input_item_t *MRLParse( intf_thread_t *p_intf, const char *mrl,
885                                    char *psz_name )
886 {
887     char *psz = strdup( mrl ), *s_mrl = psz, *s_temp;
888     if( psz == NULL )
889         return NULL;
890     /* extract the mrl */
891     s_temp = FirstOption( s_mrl, s_mrl );
892     if( s_temp == NULL )
893     {
894         s_temp = s_mrl + strlen( s_mrl );
895     }
896
897     input_item_t *p_input = input_item_New( p_intf, s_mrl, psz_name );
898     if( p_input == NULL )
899         return NULL;
900     s_mrl = s_temp;
901
902     /* now we can take care of the options */
903     while ( *s_mrl != '\0' )
904     {
905         s_temp = FirstOption( s_mrl, s_mrl );
906         if( s_mrl == '\0' )
907             break;
908         if( s_temp == NULL )
909         {
910             s_temp = s_mrl + strlen( s_mrl );
911         }
912         input_item_AddOption( p_input, s_mrl, VLC_INPUT_OPTION_TRUSTED );
913         s_mrl = s_temp;
914     }
915
916     return p_input;
917 }
918
919 /**********************************************************************
920  * RealPath: parse ../, ~ and path stuff
921  **********************************************************************/
922 char *RealPath( const char *psz_src )
923 {
924     char *psz_dir;
925     char *p;
926     int i_len = strlen(psz_src);
927
928     psz_dir = xmalloc( i_len + 2 );
929     strcpy( psz_dir, psz_src );
930
931     /* Add a trailing sep to ease the .. step */
932     psz_dir[i_len] = DIR_SEP_CHAR;
933     psz_dir[i_len + 1] = '\0';
934
935 #if (DIR_SEP_CHAR != '/')
936     /* Convert all / to native separator */
937     p = psz_dir;
938     while( (p = strchr( p, '/' )) != NULL )
939     {
940         *p = DIR_SEP_CHAR;
941     }
942 #endif
943
944     /* FIXME: this could be O(N) rather than O(N²)... */
945     /* Remove multiple separators and /./ */
946     p = psz_dir;
947     while( (p = strchr( p, DIR_SEP_CHAR )) != NULL )
948     {
949         if( p[1] == DIR_SEP_CHAR )
950             memmove( &p[1], &p[2], strlen(&p[2]) + 1 );
951         else if( p[1] == '.' && p[2] == DIR_SEP_CHAR )
952             memmove( &p[1], &p[3], strlen(&p[3]) + 1 );
953         else
954             p++;
955     }
956
957     if( psz_dir[0] == '~' )
958     {
959         char *home = config_GetUserDir( VLC_HOME_DIR ), *dir;
960         if( asprintf( &dir, "%s%s", home, psz_dir + 1 ) != -1 )
961         {
962             free( psz_dir );
963             psz_dir = dir;
964         }
965         free( home );
966     }
967
968     if( strlen(psz_dir) > 2 )
969     {
970         /* Fix all .. dir */
971         p = psz_dir + 3;
972         while( (p = strchr( p, DIR_SEP_CHAR )) != NULL )
973         {
974             if( p[-1] == '.' && p[-2] == '.' && p[-3] == DIR_SEP_CHAR )
975             {
976                 char *q;
977                 p[-3] = '\0';
978                 if( (q = strrchr( psz_dir, DIR_SEP_CHAR )) != NULL )
979                 {
980                     memmove( q + 1, p + 1, strlen(p + 1) + 1 );
981                     p = q + 1;
982                 }
983                 else
984                 {
985                     memmove( psz_dir, p, strlen(p) + 1 );
986                     p = psz_dir + 3;
987                 }
988             }
989             else
990                 p++;
991         }
992     }
993
994     /* Remove trailing sep if there are at least 2 sep in the string
995      * (handles the C:\ stuff) */
996     p = strrchr( psz_dir, DIR_SEP_CHAR );
997     if( p != NULL && p[1] == '\0' && p != strchr( psz_dir, DIR_SEP_CHAR ) )
998         *p = '\0';
999
1000     return psz_dir;
1001 }