1 /*****************************************************************************
2 * http.c : HTTP/HTTPS Remote control interface
3 *****************************************************************************
4 * Copyright (C) 2001-2006 the VideoLAN team
6 * Authors: Gildas Bazin <gbazin@netcourrier.com>
7 * Laurent Aimar <fenrir@via.ecp.fr>
8 * Christophe Massiot <massiot@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
29 #include <vlc_plugin.h>
35 /*****************************************************************************
37 *****************************************************************************/
38 static int Open ( vlc_object_t
* );
39 static void Close( vlc_object_t
* );
41 #define HOST_TEXT N_( "Host address" )
42 #define HOST_LONGTEXT N_( \
43 "Address and port the HTTP interface will listen on. It defaults to " \
44 "all network interfaces (0.0.0.0)." \
45 " If you want the HTTP interface to be available only on the local " \
46 "machine, enter 127.0.0.1" )
47 #define SRC_TEXT N_( "Source directory" )
48 #define SRC_LONGTEXT N_( "Source directory" )
49 #define HANDLERS_TEXT N_( "Handlers" )
50 #define HANDLERS_LONGTEXT N_( \
51 "List of handler extensions and executable paths (for instance: " \
52 "php=/usr/bin/php,pl=/usr/bin/perl)." )
53 #define ART_TEXT N_( "Export album art as /art" )
54 #define ART_LONGTEXT N_( \
55 "Allow exporting album art for current playlist items at the " \
56 "/art and /art?id=<id> URLs." )
57 #define CERT_TEXT N_( "Certificate file" )
58 #define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \
60 #define KEY_TEXT N_( "Private key file" )
61 #define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file." )
62 #define CA_TEXT N_( "Root CA file" )
63 #define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \
64 "certificates file." )
65 #define CRL_TEXT N_( "CRL file" )
66 #define CRL_LONGTEXT N_( "HTTP interace Certificates Revocation List file." )
69 set_shortname( N_("HTTP"))
70 set_description( N_("HTTP remote control interface") )
71 set_category( CAT_INTERFACE
)
72 set_subcategory( SUBCAT_INTERFACE_MAIN
)
73 add_string ( "http-host", NULL
, NULL
, HOST_TEXT
, HOST_LONGTEXT
, true )
74 add_string ( "http-src", NULL
, NULL
, SRC_TEXT
, SRC_LONGTEXT
, true )
75 add_obsolete_string ( "http-charset" )
76 #if defined( HAVE_FORK ) || defined( WIN32 )
77 add_string ( "http-handlers", NULL
, NULL
, HANDLERS_TEXT
, HANDLERS_LONGTEXT
, true )
79 add_bool ( "http-album-art", false, NULL
, ART_TEXT
, ART_LONGTEXT
, true )
80 set_section( N_("HTTP SSL" ), 0 )
81 add_string ( "http-intf-cert", NULL
, NULL
, CERT_TEXT
, CERT_LONGTEXT
, true )
82 add_string ( "http-intf-key", NULL
, NULL
, KEY_TEXT
, KEY_LONGTEXT
, true )
83 add_string ( "http-intf-ca", NULL
, NULL
, CA_TEXT
, CA_LONGTEXT
, true )
84 add_string ( "http-intf-crl", NULL
, NULL
, CRL_TEXT
, CRL_LONGTEXT
, true )
85 set_capability( "interface", 0 )
86 set_callbacks( Open
, Close
)
87 add_shortcut( "http" )
91 /*****************************************************************************
93 *****************************************************************************/
94 int ArtCallback( httpd_handler_sys_t
*p_args
,
95 httpd_handler_t
*p_handler
, char *_p_url
,
96 uint8_t *_p_request
, int i_type
,
97 uint8_t *_p_in
, int i_in
,
98 char *psz_remote_addr
, char *psz_remote_host
,
99 uint8_t **pp_data
, int *pi_data
);
101 /*****************************************************************************
102 * Activate: initialize and create stuff
103 *****************************************************************************/
104 static int Open( vlc_object_t
*p_this
)
106 intf_thread_t
*p_intf
= (intf_thread_t
*)p_this
;
109 char *psz_cert
= NULL
, *psz_key
= NULL
, *psz_ca
= NULL
,
112 char *psz_src
= NULL
;
114 psz_address
= var_CreateGetNonEmptyString( p_intf
, "http-host" );
115 if( psz_address
!= NULL
)
117 char *psz_parser
= strrchr( psz_address
, ':' );
120 *psz_parser
++ = '\0';
121 i_port
= atoi( psz_parser
);
125 psz_address
= strdup("");
127 p_intf
->p_sys
= p_sys
= malloc( sizeof( intf_sys_t
) );
131 return( VLC_ENOMEM
);
134 p_sys
->p_playlist
= pl_Get( p_this
);
135 p_sys
->p_input
= NULL
;
137 p_sys
->psz_address
= psz_address
;
138 p_sys
->i_port
= i_port
;
139 p_sys
->p_art_handler
= NULL
;
141 /* determine file handler associations */
142 p_sys
->i_handlers
= 0;
143 p_sys
->pp_handlers
= NULL
;
144 #if defined( HAVE_FORK ) || defined( WIN32 )
145 psz_src
= var_InheritString( p_intf
, "http-handlers" );
146 if( psz_src
!= NULL
)
151 http_association_t
*p_handler
;
153 char *psz_program
, *psz_options
;
154 p
= strchr( p
, '=' );
155 if( p
== NULL
) break;
158 p
= strchr( p
, ',' );
162 p_handler
= malloc( sizeof( http_association_t
) );
163 p_handler
->psz_ext
= strdup( psz_ext
);
164 psz_options
= FirstWord( psz_program
, psz_program
);
165 p_handler
->i_argc
= 0;
166 p_handler
->ppsz_argv
= NULL
;
167 TAB_APPEND( p_handler
->i_argc
, p_handler
->ppsz_argv
,
168 strdup( psz_program
) );
169 while( psz_options
!= NULL
&& *psz_options
)
171 char *psz_next
= FirstWord( psz_options
, psz_options
);
172 TAB_APPEND( p_handler
->i_argc
, p_handler
->ppsz_argv
,
173 strdup( psz_options
) );
174 psz_options
= psz_next
;
176 /* NULL will be appended later on */
178 TAB_APPEND( p_sys
->i_handlers
, p_sys
->pp_handlers
, p_handler
);
184 /* determine SSL configuration */
185 psz_cert
= var_InheritString( p_intf
, "http-intf-cert" );
186 if ( psz_cert
!= NULL
)
188 msg_Dbg( p_intf
, "enabling TLS for HTTP interface (cert file: %s)",
190 psz_key
= var_InheritString( p_intf
, "http-intf-key" );
191 psz_ca
= var_InheritString( p_intf
, "http-intf-ca" );
192 psz_crl
= var_InheritString( p_intf
, "http-intf-crl" );
203 msg_Dbg( p_intf
, "base %s:%d", psz_address
, i_port
);
205 p_sys
->p_httpd_host
= httpd_TLSHostNew( VLC_OBJECT(p_intf
), psz_address
,
206 i_port
, psz_cert
, psz_key
, psz_ca
,
213 if( p_sys
->p_httpd_host
== NULL
)
215 msg_Err( p_intf
, "cannot listen on %s:%d", psz_address
, i_port
);
216 free( p_sys
->psz_address
);
222 char psz_tmp
[NI_MAXHOST
+ 6];
224 /* Ugly hack to run several HTTP servers on different ports */
225 snprintf( psz_tmp
, sizeof (psz_tmp
), "%s:%d", psz_address
, i_port
+ 1 );
226 var_Create(p_intf
->p_libvlc
, "http-host", VLC_VAR_STRING
);
227 var_SetString( p_intf
->p_libvlc
, "http-host", psz_tmp
);
231 p_sys
->pp_files
= NULL
;
233 psz_src
= var_InheritString( p_intf
, "http-src" );
234 if( psz_src
== NULL
)
236 char *data_path
= config_GetDataDir( p_intf
);
237 if( asprintf( &psz_src
, "%s" DIR_SEP
"http", data_path
) == -1 )
242 if( psz_src
== NULL
)
244 msg_Err( p_intf
, "invalid web interface source directory" );
248 /* remove trainling \ or / */
249 if( psz_src
[strlen( psz_src
) - 1] == '\\' ||
250 psz_src
[strlen( psz_src
) - 1] == '/' )
252 psz_src
[strlen( psz_src
) - 1] = '\0';
255 ParseDirectory( p_intf
, psz_src
, psz_src
);
256 if( p_sys
->i_files
<= 0 )
258 msg_Err( p_intf
, "cannot find any file in directory %s", psz_src
);
264 if( var_InheritBool( p_intf
, "http-album-art" ) )
266 /* FIXME: we're leaking h */
267 httpd_handler_sys_t
*h
= malloc( sizeof( httpd_handler_sys_t
) );
270 h
->file
.p_intf
= p_intf
;
273 /* TODO: use ACL and login/password stuff here too */
274 h
->p_handler
= httpd_HandlerNew( p_sys
->p_httpd_host
,
275 "/art", NULL
, NULL
, NULL
,
277 p_sys
->p_art_handler
= h
->p_handler
;
284 free( p_sys
->pp_files
);
285 httpd_HostDelete( p_sys
->p_httpd_host
);
286 free( p_sys
->psz_address
);
291 /*****************************************************************************
292 * Close: destroy interface
293 *****************************************************************************/
294 static void Close ( vlc_object_t
*p_this
)
296 intf_thread_t
*p_intf
= (intf_thread_t
*)p_this
;
297 intf_sys_t
*p_sys
= p_intf
->p_sys
;
302 vlm_Delete( p_sys
->p_vlm
);
304 for( i
= 0; i
< p_sys
->i_files
; i
++ )
306 if( p_sys
->pp_files
[i
]->b_handler
)
307 httpd_HandlerDelete( ((httpd_handler_sys_t
*)p_sys
->pp_files
[i
])->p_handler
);
309 httpd_FileDelete( p_sys
->pp_files
[i
]->p_file
);
310 if( p_sys
->pp_files
[i
]->p_redir
)
311 httpd_RedirectDelete( p_sys
->pp_files
[i
]->p_redir
);
312 if( p_sys
->pp_files
[i
]->p_redir2
)
313 httpd_RedirectDelete( p_sys
->pp_files
[i
]->p_redir2
);
315 free( p_sys
->pp_files
[i
]->file
);
316 free( p_sys
->pp_files
[i
]->name
);
317 free( p_sys
->pp_files
[i
] );
319 free( p_sys
->pp_files
);
320 for( i
= 0; i
< p_sys
->i_handlers
; i
++ )
322 http_association_t
*p_handler
= p_sys
->pp_handlers
[i
];
324 free( p_handler
->psz_ext
);
325 for( j
= 0; j
< p_handler
->i_argc
; j
++ )
326 free( p_handler
->ppsz_argv
[j
] );
327 if( p_handler
->i_argc
)
328 free( p_handler
->ppsz_argv
);
331 if( p_sys
->i_handlers
)
332 free( p_sys
->pp_handlers
);
333 if( p_sys
->p_art_handler
)
334 httpd_HandlerDelete( p_sys
->p_art_handler
);
335 httpd_HostDelete( p_sys
->p_httpd_host
);
336 free( p_sys
->psz_address
);
340 /****************************************************************************
342 ****************************************************************************
343 * a file with b_html is parsed and all "macro" replaced
344 ****************************************************************************/
345 static void Callback404( httpd_file_sys_t
*p_args
, char **pp_data
,
348 char *p
= *pp_data
= malloc( 10240 );
353 p
+= sprintf( p
, "Content-Type: text/html\n" );
354 p
+= sprintf( p
, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" );
355 p
+= sprintf( p
, "<head>\n" );
356 p
+= sprintf( p
, "<title>Error loading %s</title>\n", p_args
->file
);
357 p
+= sprintf( p
, "</head>\n" );
358 p
+= sprintf( p
, "<body>\n" );
359 p
+= sprintf( p
, "<h1><center>Error loading %s for %s</center></h1>\n", p_args
->file
, p_args
->name
);
360 p
+= sprintf( p
, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
361 p
+= sprintf( p
, "</body>\n" );
362 p
+= sprintf( p
, "</html>\n" );
364 *pi_data
= strlen( *pp_data
);
367 static void ParseExecute( httpd_file_sys_t
*p_args
, char *p_buffer
,
368 int i_buffer
, char *p_request
,
369 char **pp_data
, int *pi_data
)
371 intf_sys_t
*p_sys
= p_args
->p_intf
->p_sys
;
372 int i_request
= p_request
!= NULL
? strlen( p_request
) : 0;
374 char position
[4]; /* percentage */
375 char time
[12]; /* in seconds */
376 char length
[12]; /* in seconds */
377 audio_volume_t i_volume
;
382 assert( p_sys
->p_input
== NULL
);
383 /* FIXME: proper locking anyone? */
384 p_sys
->p_input
= playlist_CurrentInput( p_sys
->p_playlist
);
387 snprintf( position
, sizeof(position
), "%d",
388 (int)(var_GetFloat( p_sys
->p_input
, "position" ) * 100.));
389 snprintf( time
, sizeof(time
), "%"PRIi64
,
390 var_GetTime( p_sys
->p_input
, "time" ) / CLOCK_FREQ
);
391 snprintf( length
, sizeof(length
), "%"PRIi64
,
392 var_GetTime( p_sys
->p_input
, "length" ) / CLOCK_FREQ
);
394 switch( var_GetInteger( p_sys
->p_input
, "state" ) )
396 case PLAYING_S
: state
= "playing"; break;
397 case OPENING_S
: state
= "opening/connecting"; break;
398 case PAUSE_S
: state
= "paused"; break;
399 default: state
= "stop"; break;
404 strcpy( position
, "0" );
406 strcpy( length
, "0" );
410 aout_VolumeGet( p_sys
->p_playlist
, &i_volume
);
411 snprintf( volume
, sizeof(volume
), "%d", (int)i_volume
);
413 p_args
->vars
= mvar_New( "variables", "" );
414 mvar_AppendNewVar( p_args
->vars
, "url_param",
415 i_request
> 0 ? "1" : "0" );
416 mvar_AppendNewVar( p_args
->vars
, "url_value", p_request
);
417 mvar_AppendNewVar( p_args
->vars
, "version", VLC_Version() );
418 mvar_AppendNewVar( p_args
->vars
, "copyright", COPYRIGHT_MESSAGE
);
419 mvar_AppendNewVar( p_args
->vars
, "vlc_compile_by", VLC_CompileBy() );
420 mvar_AppendNewVar( p_args
->vars
, "vlc_compile_host",
422 mvar_AppendNewVar( p_args
->vars
, "vlc_compiler", VLC_Compiler() );
423 mvar_AppendNewVar( p_args
->vars
, "stream_position", position
);
424 mvar_AppendNewVar( p_args
->vars
, "stream_time", time
);
425 mvar_AppendNewVar( p_args
->vars
, "stream_length", length
);
426 mvar_AppendNewVar( p_args
->vars
, "volume", volume
);
427 mvar_AppendNewVar( p_args
->vars
, "stream_state", state
);
428 mvar_AppendNewVar( p_args
->vars
, "charset", "UTF-8" );
433 /* FIXME: Workarround a stupid assert in input_GetItem */
434 input_item_t
*p_item
= p_sys
->p_input
&& p_sys
->p_input
->p
435 ? input_GetItem( p_sys
->p_input
)
440 vlc_mutex_lock( &p_item
->p_stats
->lock
);
441 #define STATS_INT( n ) sprintf( stats, "%"PRIi64, p_item->p_stats->i_ ## n ); \
442 mvar_AppendNewVar( p_args->vars, #n, stats );
443 #define STATS_FLOAT( n ) sprintf( stats, "%f", p_item->p_stats->f_ ## n ); \
444 mvar_AppendNewVar( p_args->vars, #n, stats );
445 STATS_INT( read_bytes
)
446 STATS_FLOAT( input_bitrate
)
447 STATS_INT( demux_read_bytes
)
448 STATS_FLOAT( demux_bitrate
)
449 STATS_INT( decoded_video
)
450 STATS_INT( displayed_pictures
)
451 STATS_INT( lost_pictures
)
452 STATS_INT( decoded_audio
)
453 STATS_INT( played_abuffers
)
454 STATS_INT( lost_abuffers
)
455 STATS_INT( sent_packets
)
456 STATS_INT( sent_bytes
)
457 STATS_FLOAT( send_bitrate
)
460 vlc_mutex_unlock( &p_item
->p_stats
->lock
);
464 SSInit( &p_args
->stack
);
466 /* allocate output */
467 *pi_data
= i_buffer
+ 1000;
468 dst
= *pp_data
= malloc( *pi_data
);
470 /* we parse executing all <vlc /> macros */
471 Execute( p_args
, p_request
, i_request
, pp_data
, pi_data
, &dst
,
472 &p_buffer
[0], &p_buffer
[i_buffer
] );
475 *pi_data
= dst
- *pp_data
;
477 if( p_sys
->p_input
!= NULL
)
479 vlc_object_release( p_sys
->p_input
);
480 p_sys
->p_input
= NULL
;
482 SSClean( &p_args
->stack
);
483 mvar_Delete( p_args
->vars
);
486 int HttpCallback( httpd_file_sys_t
*p_args
,
487 httpd_file_t
*p_file
,
489 uint8_t **_pp_data
, int *pi_data
)
492 char *p_request
= (char *)_p_request
;
493 char **pp_data
= (char **)_pp_data
;
496 if( ( f
= vlc_fopen( p_args
->file
, "r" ) ) == NULL
)
498 Callback404( p_args
, pp_data
, pi_data
);
502 if( !p_args
->b_html
)
504 FileLoad( f
, pp_data
, pi_data
);
511 /* first we load in a temporary buffer */
512 FileLoad( f
, &p_buffer
, &i_buffer
);
514 ParseExecute( p_args
, p_buffer
, i_buffer
, p_request
, pp_data
, pi_data
);
524 /****************************************************************************
526 ****************************************************************************
527 * call the external handler and parse vlc macros if Content-Type is HTML
528 ****************************************************************************/
529 int HandlerCallback( httpd_handler_sys_t
*p_args
,
530 httpd_handler_t
*p_handler
, char *_p_url
,
531 uint8_t *_p_request
, int i_type
,
532 uint8_t *_p_in
, int i_in
,
533 char *psz_remote_addr
, char *psz_remote_host
,
534 uint8_t **_pp_data
, int *pi_data
)
536 VLC_UNUSED(p_handler
); VLC_UNUSED(_p_in
);
537 char *p_url
= (char *)_p_url
;
538 char *p_request
= (char *)_p_request
;
539 char **pp_data
= (char **)_pp_data
;
540 char *p_in
= (char *)_p_in
;
541 int i_request
= p_request
!= NULL
? strlen( p_request
) : 0;
544 char **ppsz_env
= NULL
;
548 char *psz_cwd
, *psz_file
= NULL
;
551 /* Create environment for the CGI */
552 TAB_APPEND( i_env
, ppsz_env
, strdup("GATEWAY_INTERFACE=CGI/1.1") );
553 TAB_APPEND( i_env
, ppsz_env
, strdup("SERVER_PROTOCOL=HTTP/1.1") );
554 TAB_APPEND( i_env
, ppsz_env
, strdup("SERVER_SOFTWARE=VLC "VERSION
) );
559 TAB_APPEND( i_env
, ppsz_env
, strdup("REQUEST_METHOD=GET") );
562 TAB_APPEND( i_env
, ppsz_env
, strdup("REQUEST_METHOD=POST") );
565 TAB_APPEND( i_env
, ppsz_env
, strdup("REQUEST_METHOD=HEAD") );
573 if( -1==asprintf( &psz_tmp
, "QUERY_STRING=%s", p_request
) )
575 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
577 if( -1==asprintf( &psz_tmp
, "REQUEST_URI=%s?%s", p_url
, p_request
) )
579 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
583 if( -1==asprintf( &psz_tmp
, "REQUEST_URI=%s", p_url
) )
585 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
588 if( -1==asprintf( &psz_tmp
, "SCRIPT_NAME=%s", p_url
) )
590 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
592 #define p_sys p_args->file.p_intf->p_sys
593 if( -1==asprintf( &psz_tmp
, "SERVER_NAME=%s", p_sys
->psz_address
) )
595 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
597 if( -1==asprintf( &psz_tmp
, "SERVER_PORT=%u", p_sys
->i_port
) )
599 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
602 p
= getenv( "PATH" );
605 if( -1==asprintf( &psz_tmp
, "PATH=%s", p
) )
607 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
611 p
= getenv( "windir" );
614 if( -1==asprintf( &psz_tmp
, "SYSTEMROOT=%s", p
) )
616 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
620 if( psz_remote_addr
!= NULL
&& *psz_remote_addr
)
622 if( -1==asprintf( &psz_tmp
, "REMOTE_ADDR=%s", psz_remote_addr
) )
624 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
627 if( psz_remote_host
!= NULL
&& *psz_remote_host
)
629 if( -1==asprintf( &psz_tmp
, "REMOTE_HOST=%s", psz_remote_host
) )
631 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
639 if( !strncasecmp( p
, "Content-Type: ", strlen("Content-Type: ") ) )
641 char *end
= strchr( p
, '\r' );
645 if( -1==asprintf( &psz_tmp
, "CONTENT_TYPE=%s", p
) )
647 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
650 if( !strncasecmp( p
, "Content-Length: ",
651 strlen("Content-Length: ") ) )
653 char *end
= strchr( p
, '\r' );
657 if( -1==asprintf( &psz_tmp
, "CONTENT_LENGTH=%s", p
) )
659 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
663 p
= strchr( p
, '\n' );
664 if( p
== NULL
|| p
[1] == '\r' )
673 psz_file
= strrchr( p_args
->file
.file
, DIR_SEP_CHAR
);
674 if( psz_file
!= NULL
)
677 if( -1==asprintf( &psz_tmp
, "SCRIPT_FILENAME=%s", psz_file
) )
679 TAB_APPEND( i_env
, ppsz_env
, psz_tmp
);
681 TAB_APPEND( p_args
->p_association
->i_argc
,
682 p_args
->p_association
->ppsz_argv
, psz_file
);
685 TAB_APPEND( i_env
, ppsz_env
, NULL
);
687 TAB_APPEND( p_args
->p_association
->i_argc
, p_args
->p_association
->ppsz_argv
,
690 psz_tmp
= strdup( p_args
->file
.file
);
691 p
= strrchr( psz_tmp
, DIR_SEP_CHAR
);
703 i_ret
= vlc_execve( p_args
->file
.p_intf
, p_args
->p_association
->i_argc
,
704 p_args
->p_association
->ppsz_argv
, ppsz_env
, psz_cwd
,
705 (char *)p_in
, i_in
, &p_buffer
, &i_buffer
);
706 TAB_REMOVE( p_args
->p_association
->i_argc
, p_args
->p_association
->ppsz_argv
,
708 TAB_REMOVE( p_args
->p_association
->i_argc
, p_args
->p_association
->ppsz_argv
,
712 TAB_REMOVE( i_env
, ppsz_env
, ppsz_env
[0] );
716 Callback404( (httpd_file_sys_t
*)p_args
, pp_data
, pi_data
);
720 while( strncasecmp( p
, "Content-Type: text/html",
721 strlen("Content-Type: text/html") ) )
723 p
= strchr( p
, '\n' );
724 if( p
== NULL
|| p
[1] == '\r' )
739 ParseExecute( (httpd_file_sys_t
*)p_args
, p_buffer
, i_buffer
,
740 p_request
, pp_data
, pi_data
);
748 int ArtCallback( httpd_handler_sys_t
*p_args
,
749 httpd_handler_t
*p_handler
, char *_p_url
,
750 uint8_t *p_request
, int i_type
,
751 uint8_t *p_in
, int i_in
,
752 char *psz_remote_addr
, char *psz_remote_host
,
753 uint8_t **pp_data
, int *pi_data
)
755 VLC_UNUSED(p_handler
); VLC_UNUSED(_p_url
); VLC_UNUSED(i_type
);
756 VLC_UNUSED(p_in
); VLC_UNUSED(i_in
); VLC_UNUSED(psz_remote_addr
);
757 VLC_UNUSED(psz_remote_host
);
759 char *psz_art
= NULL
;
760 intf_thread_t
*p_intf
= p_args
->file
.p_intf
;
761 intf_sys_t
*p_sys
= p_intf
->p_sys
;
763 input_item_t
*p_item
= NULL
;
768 ExtractURIValue( (char *)p_request
, "id", psz_id
, 15 );
769 i_id
= atoi( psz_id
);
772 playlist_Lock( p_sys
->p_playlist
);
773 playlist_item_t
*p_pl_item
= playlist_ItemGetById( p_sys
->p_playlist
,
776 p_item
= p_pl_item
->p_input
;
777 playlist_Unlock( p_sys
->p_playlist
);
781 /* FIXME: Workarround a stupid assert in input_GetItem */
782 if( p_sys
->p_input
&& p_sys
->p_input
->p
)
783 p_item
= input_GetItem( p_sys
->p_input
);
788 psz_art
= input_item_GetArtURL( p_item
);
793 char *psz
= make_path( psz_art
);
798 if( psz_art
== NULL
)
800 msg_Dbg( p_intf
, "No album art found" );
801 Callback404( &p_args
->file
, (char**)pp_data
, pi_data
);
805 FILE *f
= vlc_fopen( psz_art
, "r" );
808 msg_Dbg( p_intf
, "Couldn't open album art file %s", psz_art
);
809 Callback404( &p_args
->file
, (char**)pp_data
, pi_data
);
817 FileLoad( f
, &p_data
, &i_data
);
820 char *psz_ext
= strrchr( psz_art
, '.' );
821 if( psz_ext
) psz_ext
++;
823 #define HEADER "Content-Type: image/%s\n" \
824 "Content-Length: %d\n" \
827 int i_header_size
= asprintf( &psz_header
, HEADER
, psz_ext
, i_data
);
829 if( likely(i_header_size
!= -1) )
831 *pp_data
= malloc( i_header_size
+ i_data
);
832 if( likely(*pp_data
!= NULL
) )
834 *pi_data
= i_header_size
+ i_data
;
835 memcpy( *pp_data
, psz_header
, i_header_size
);
836 memcpy( *pp_data
+i_header_size
, p_data
, i_data
);