]> git.sesse.net Git - vlc/blob - modules/control/http.c
* modules/gui/wxwindows/interface.cpp: fixed compile issue.
[vlc] / modules / control / http.c
1 /*****************************************************************************
2  * http.c :  http remote control plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: http.c,v 1.9 2003/05/23 23:53:53 sigmunau Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/intf.h>
37
38 #ifdef HAVE_UNISTD_H
39 #    include <unistd.h>
40 #endif
41
42 #ifdef HAVE_SYS_TIME_H
43 #    include <sys/time.h>
44 #endif
45 #include <sys/types.h>
46
47 #include "httpd.h"
48
49 /*****************************************************************************
50  * Local prototypes
51  *****************************************************************************/
52 static int  Activate     ( vlc_object_t * );
53 static void Close        ( vlc_object_t * );
54 static void Run          ( intf_thread_t *p_intf );
55
56 static int  httpd_page_interface_get( httpd_file_callback_args_t *p_args,
57                                       uint8_t *p_request, int i_request,
58                                       uint8_t **pp_data, int *pi_data );
59
60 /*****************************************************************************
61  * intf_sys_t: description and status of interface
62  *****************************************************************************/
63 struct intf_sys_t
64 {
65     httpd_t             *p_httpd;
66     httpd_host_t        *p_httpd_host;
67
68     input_thread_t *    p_input;
69 };
70
71 /*****************************************************************************
72  * Module descriptor
73  *****************************************************************************/
74 #define PORT_TEXT N_( "HTTP interface bind port" )
75 #define PORT_LONGTEXT N_( \
76     "You can set the port on which the http interface will accept connections" )
77 #define ADDR_TEXT N_( "HTTP interface bind address" )
78 #define ADDR_LONGTEXT N_( \
79     "You can set the address on which the http interface will bind" )
80
81 vlc_module_begin();
82     add_category_hint( N_("HTTP remote control"), NULL, VLC_TRUE );
83     add_string( "http-addr", NULL, NULL, ADDR_TEXT, ADDR_LONGTEXT, VLC_TRUE );
84     add_integer( "http-port", 8080, NULL, PORT_TEXT, PORT_LONGTEXT, VLC_TRUE );
85     set_description( _("HTTP remote control interface") );
86     set_capability( "interface", 10 );
87     set_callbacks( Activate, Close );
88 vlc_module_end();
89
90 /*****************************************************************************
91  * Activate: initialize and create stuff
92  *****************************************************************************/
93 static int Activate( vlc_object_t *p_this )
94 {
95     intf_thread_t *p_intf = (intf_thread_t*)p_this;
96
97     /* Allocate instance and initialize some members */
98     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
99     if( p_intf->p_sys == NULL )
100     {
101         return VLC_EGENERIC;
102     };
103
104     p_intf->p_sys->p_httpd = NULL;
105     p_intf->p_sys->p_httpd_host = NULL;
106
107     p_intf->pf_run = Run;
108
109     CONSOLE_INTRO_MSG;
110
111     return VLC_SUCCESS;
112 }
113
114 /*****************************************************************************
115  * CloseIntf: destroy interface
116  *****************************************************************************/
117 void Close ( vlc_object_t *p_this )
118 {
119     intf_thread_t *p_intf = (intf_thread_t *)p_this;
120
121     if( p_intf->p_sys->p_httpd_host )
122         p_intf->p_sys->p_httpd->pf_unregister_host( p_intf->p_sys->p_httpd,
123                                                  p_intf->p_sys->p_httpd_host );
124
125     if( p_intf->p_sys->p_httpd )
126         httpd_Release( p_intf->p_sys->p_httpd );
127
128     /* Destroy structure */
129     free( p_intf->p_sys );
130 }
131
132 /*****************************************************************************
133  * Run: http interface thread
134  *****************************************************************************/
135 static void Run( intf_thread_t *p_intf )
136 {
137     input_thread_t *p_input = NULL;
138     playlist_t     *p_playlist = NULL;
139     httpd_file_t   *p_page_intf;
140
141 #if 0
142     input_info_category_t * p_category;
143     input_info_t * p_info;
144
145     off_t      i_oldpos = 0;
146     off_t      i_newpos;
147     double     f_ratio = 1.0;
148 #endif
149
150     /* Get bind address and port */
151     char *psz_bind_addr = config_GetPsz( p_intf, "http-addr" );
152     int i_bind_port = config_GetInt( p_intf, "http-port" );
153     if( !psz_bind_addr ) psz_bind_addr = strdup( "" );
154
155     p_intf->p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_intf), VLC_TRUE );
156     if( !p_intf->p_sys->p_httpd )
157     {
158         msg_Err( p_intf, "cannot start httpd daemon" );
159         free( p_intf->p_sys );
160         return;
161     }
162
163     p_intf->p_sys->p_httpd_host =
164         p_intf->p_sys->p_httpd->pf_register_host( p_intf->p_sys->p_httpd,
165                                                   psz_bind_addr, i_bind_port );
166     
167     if( !p_intf->p_sys->p_httpd_host )
168     {
169         msg_Err( p_intf, "cannot listen on %s:%d", psz_bind_addr, i_bind_port);
170         httpd_Release( p_intf->p_sys->p_httpd );
171         free( p_intf->p_sys );
172         return;
173     }
174
175     msg_Info( p_intf, "http interface started on %s:%d", 
176         *psz_bind_addr ? psz_bind_addr : "localhost" , i_bind_port );
177
178     /*
179      * Register our interface page with the httpd daemon
180      */
181     p_page_intf = p_intf->p_sys->p_httpd->pf_register_file(
182                       p_intf->p_sys->p_httpd, "/", "text/html",
183                       NULL, NULL, httpd_page_interface_get,
184                       httpd_page_interface_get,
185                       (httpd_file_callback_args_t*)p_intf );
186
187     while( !p_intf->b_die )
188     {
189
190         /* Manage the input part */
191         if( p_input == NULL )
192         {
193             if( p_playlist )
194             {
195                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
196                                                        FIND_CHILD );
197             }
198             else
199             {
200                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
201                                                    FIND_ANYWHERE );
202                 if( p_input )
203                 {
204                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
205                                                            FIND_PARENT );
206                 }
207             }
208         }
209         else if( p_input->b_dead )
210         {
211             vlc_object_release( p_input );
212             p_input = NULL;
213         }
214
215         if( p_input )
216         {
217             /* Get position */
218             vlc_mutex_lock( &p_input->stream.stream_lock );
219             if( !p_input->b_die && p_input->stream.i_mux_rate )
220             {
221 #if 0
222 #define A p_input->stream.p_selected_area
223                 f_ratio = 1.0 / ( 50 * p_input->stream.i_mux_rate );
224                 i_newpos = A->i_tell * f_ratio;
225
226                 if( i_oldpos != i_newpos )
227                 {
228                     i_oldpos = i_newpos;
229                     printf( "pos: %li s / %li s\n", (long int)i_newpos,
230                             (long int)(f_ratio * A->i_size) );
231                 }
232 #undef S
233 #endif
234             }
235             vlc_mutex_unlock( &p_input->stream.stream_lock );
236         }
237
238         /* Wait a bit */
239         msleep( INTF_IDLE_SLEEP );
240     }
241
242     p_intf->p_sys->p_httpd->pf_unregister_file( p_intf->p_sys->p_httpd,
243                                                 p_page_intf );
244
245     if( p_input )
246     {
247         vlc_object_release( p_input );
248         p_input = NULL;
249     }
250
251     if( p_playlist )
252     {
253         vlc_object_release( p_playlist );
254         p_playlist = NULL;
255     }
256 }
257
258 /*****************************************************************************
259  * Local functions
260  *****************************************************************************/
261 static int httpd_page_interface_update( intf_thread_t *p_intf,
262                                         playlist_t *p_playlist,
263                                         uint8_t **pp_data, int *pi_data, vlc_bool_t b_redirect );
264
265 static void uri_extract_value( char *psz_uri, char *psz_name,
266                                char *psz_value, int i_value_max )
267 {
268     char *p;
269
270     p = strstr( psz_uri, psz_name );
271     if( p )
272     {
273         int i_len;
274
275         p += strlen( psz_name );
276         if( *p == '=' ) p++;
277
278         if( strchr( p, '&' ) )
279         {
280             i_len = strchr( p, '&' ) - p;
281         }
282         else
283         {
284             /* for POST method */
285             if( strchr( p, '\n' ) )
286             {
287                 i_len = strchr( p, '\n' ) - p;
288                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
289             }
290             else
291             {
292                 i_len = strlen( p );
293             }
294         }
295         i_len = __MIN( i_value_max - 1, i_len );
296         if( i_len > 0 )
297         {
298             strncpy( psz_value, p, i_len );
299             psz_value[i_len] = '\0';
300         }
301         else
302         {
303             strncpy( psz_value, "", i_value_max );
304         }
305     }
306     else
307     {
308         strncpy( psz_value, "", i_value_max );
309     }
310 }
311
312 static void uri_decode_url_encoded( char *psz )
313 {
314     char *dup = strdup( psz );
315     char *p = dup;
316
317     while( *p )
318     {
319         if( *p == '%' )
320         {
321             char val[3];
322             p++;
323             if( !*p )
324             {
325                 break;
326             }
327
328             val[0] = *p++;
329             val[1] = *p++;
330             val[2] = '\0';
331
332             *psz++ = strtol( val, NULL, 16 );
333         }
334         else if( *p == '+' )
335         {
336             *psz++ = ' ';
337             p++;
338         }
339         else
340         {
341             *psz++ = *p++;
342         }
343     }
344     *psz++  ='\0';
345     free( dup );
346 }
347
348 static int httpd_page_interface_get( httpd_file_callback_args_t *p_args,
349                                      uint8_t *p_request, int i_request,
350                                      uint8_t **pp_data, int *pi_data )
351 {
352     intf_thread_t *p_intf = (intf_thread_t *)p_args;
353     playlist_t *p_playlist;
354     int i_ret;
355
356     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
357     if( !p_playlist )
358     {
359         return VLC_EGENERIC;
360     }
361
362     if( i_request > 0)
363     {
364         char action[512];
365
366         uri_extract_value( p_request, "action", action, 512 );
367
368         if( !strcmp( action, "play" ) )
369         {
370             int i_item;
371             uri_extract_value( p_request, "item", action, 512 );
372             i_item = atol( action );
373             playlist_Command( p_playlist, PLAYLIST_GOTO, i_item );
374             msg_Dbg( p_intf, "requested playlist item: %i", i_item );
375         }
376         else if( !strcmp( action, "stop" ) )
377         {
378             playlist_Command( p_playlist, PLAYLIST_STOP, 0 );
379             msg_Dbg( p_intf, "requested playlist stop" );
380         }
381         else if( !strcmp( action, "pause" ) )
382         {
383             playlist_Command( p_playlist, PLAYLIST_PAUSE, 0 );
384             msg_Dbg( p_intf, "requested playlist pause" );
385         }
386         else if( !strcmp( action, "next" ) )
387         {
388             playlist_Command( p_playlist, PLAYLIST_GOTO,
389                               p_playlist->i_index + 1 );
390             msg_Dbg( p_intf, "requested playlist next" );
391         }
392         else if( !strcmp( action, "previous" ) )
393         {
394             playlist_Command( p_playlist, PLAYLIST_GOTO,
395                               p_playlist->i_index - 1 );
396             msg_Dbg( p_intf, "requested playlist previous" );
397         }
398         else if( !strcmp( action, "add" ) )
399         {
400             uri_extract_value( p_request, "mrl", action, 512 );
401             uri_decode_url_encoded( action );
402             playlist_Add( p_playlist, action,
403                           PLAYLIST_APPEND, PLAYLIST_END );
404             msg_Dbg( p_intf, "requested playlist add: %s", action );
405         }
406     }
407
408     i_ret = httpd_page_interface_update( p_intf, p_playlist, pp_data, pi_data, i_request ? VLC_TRUE : VLC_FALSE );
409
410     vlc_object_release( p_playlist );
411
412     return i_ret;
413 }
414
415 static int httpd_page_interface_update( intf_thread_t *p_intf,
416                                         playlist_t *p_playlist,
417                                         uint8_t **pp_data, int *pi_data, vlc_bool_t b_redirect )
418 {
419     int i, i_size = 0;
420     char *p;
421
422     vlc_mutex_lock( &p_playlist->object_lock );
423
424     /*
425      * Count playlist items for memory allocation
426      */
427     for ( i = 0; i < p_playlist->i_size; i++ )
428     {
429         i_size += sizeof("<a href=?action=play&item=?>? - </a><br />\n" );
430         i_size += strlen( p_playlist->pp_items[i]->psz_name );
431     }
432     /* add something for all the static strings below */
433     i_size += 8192;
434
435     p = *pp_data = malloc( i_size );
436     p += sprintf( p, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" );
437     p += sprintf( p, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" );
438     p += sprintf( p, "<head>\n" );
439     p += sprintf( p, "<title>VLC Media Player</title>\n" );
440     if( b_redirect )
441     {
442         p += sprintf( p, "<meta http-equiv=\"refresh\" content=\"0;URL=/\"/>\n" );
443     }
444     p += sprintf( p, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\n" );
445     /* p += sprintf( p, "<link rel=\"shortcut icon\" href=\"http://www.videolan.org/favicon.ico\">\n" ); */
446     p += sprintf( p, "</head>\n" );
447     p += sprintf( p, "<body>\n" );
448     p += sprintf( p, "<h2 style=\"align: center;\"><a href=\"http://www.videolan.org\">"
449                      "VLC Media Player</a> (http interface)</h2>\n" );
450
451     /*
452      * Display the controls
453      */
454     p += sprintf( p, "<hr/>\n" );
455
456     p += sprintf( p, "<form method=\"get\" action=\"/\">"
457         "<p><input type=\"submit\" name=\"action\" value=\"stop\" />"
458         "<input type=\"submit\" name=\"action\" value=\"pause\" />"
459         "<input type=\"submit\" name=\"action\" value=\"previous\" />"
460         "<input type=\"submit\" name=\"action\" value=\"next\" /></p>"
461         "</form>\n" );
462
463     p += sprintf( p, "<form method=\"get\" action=\"/\" "
464                      "enctype=\"text/plain\" >"
465         "<p>Media Resource Locator:<br/>"
466         "<input type=\"text\" name=\"mrl\" size=\"40\" />"
467         "<input type=\"submit\" name=\"action\" value=\"add\" /></p>"
468         "</form>\n" );
469
470     p += sprintf( p, "<hr/>\n" );
471
472     /*
473      * Display the playlist items
474      */
475     if ( p_playlist->i_size )
476     {
477         p += sprintf( p, "<ol>\n" );
478         for ( i = 0; i < p_playlist->i_size; i++ )
479         {
480             if( i == p_playlist->i_index ) p += sprintf( p, "<b>" );
481             
482             p += sprintf( p, "<li><a href=\"?action=play&amp;item=%i\">", i );
483             p += sprintf( p, "%s", p_playlist->pp_items[i]->psz_name );
484             p += sprintf( p, "</a></li>" );
485             
486             if( i == p_playlist->i_index ) p += sprintf( p, "</b>" );
487             p += sprintf( p, "\n" );
488         }
489         p += sprintf( p, "</ol>\n" );
490     }
491     else
492     {
493         p += sprintf( p, "<p>no entries</p>\n" );
494     }
495
496     p += sprintf( p, "</body>\n" );
497     p += sprintf( p, "</html>\n" );
498
499     *pi_data = strlen( *pp_data );
500
501     vlc_mutex_unlock( &p_playlist->object_lock );
502
503     return VLC_SUCCESS;
504 }