]> git.sesse.net Git - vlc/blob - modules/control/http.c
* modules/control/http.c: strings for the address and the port variable were switched.
[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.7 2003/05/22 15:34:02 hartman 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" );
176
177     /*
178      * Register our interface page with the httpd daemon
179      */
180     p_page_intf = p_intf->p_sys->p_httpd->pf_register_file(
181                       p_intf->p_sys->p_httpd, "/", "text/html",
182                       NULL, NULL, httpd_page_interface_get,
183                       httpd_page_interface_get,
184                       (httpd_file_callback_args_t*)p_intf );
185
186     while( !p_intf->b_die )
187     {
188
189         /* Manage the input part */
190         if( p_input == NULL )
191         {
192             if( p_playlist )
193             {
194                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
195                                                        FIND_CHILD );
196             }
197             else
198             {
199                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
200                                                    FIND_ANYWHERE );
201                 if( p_input )
202                 {
203                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
204                                                            FIND_PARENT );
205                 }
206             }
207         }
208         else if( p_input->b_dead )
209         {
210             vlc_object_release( p_input );
211             p_input = NULL;
212         }
213
214         if( p_input )
215         {
216             /* Get position */
217             vlc_mutex_lock( &p_input->stream.stream_lock );
218             if( !p_input->b_die && p_input->stream.i_mux_rate )
219             {
220 #if 0
221 #define A p_input->stream.p_selected_area
222                 f_ratio = 1.0 / ( 50 * p_input->stream.i_mux_rate );
223                 i_newpos = A->i_tell * f_ratio;
224
225                 if( i_oldpos != i_newpos )
226                 {
227                     i_oldpos = i_newpos;
228                     printf( "pos: %li s / %li s\n", (long int)i_newpos,
229                             (long int)(f_ratio * A->i_size) );
230                 }
231 #undef S
232 #endif
233             }
234             vlc_mutex_unlock( &p_input->stream.stream_lock );
235         }
236
237         /* Wait a bit */
238         msleep( INTF_IDLE_SLEEP );
239     }
240
241     p_intf->p_sys->p_httpd->pf_unregister_file( p_intf->p_sys->p_httpd,
242                                                 p_page_intf );
243
244     if( p_input )
245     {
246         vlc_object_release( p_input );
247         p_input = NULL;
248     }
249
250     if( p_playlist )
251     {
252         vlc_object_release( p_playlist );
253         p_playlist = NULL;
254     }
255 }
256
257 /*****************************************************************************
258  * Local functions
259  *****************************************************************************/
260 static int httpd_page_interface_update( intf_thread_t *p_intf,
261                                         playlist_t *p_playlist,
262                                         uint8_t **pp_data, int *pi_data, vlc_bool_t b_redirect );
263
264 static void uri_extract_value( char *psz_uri, char *psz_name,
265                                char *psz_value, int i_value_max )
266 {
267     char *p;
268
269     p = strstr( psz_uri, psz_name );
270     if( p )
271     {
272         int i_len;
273
274         p += strlen( psz_name );
275         if( *p == '=' ) p++;
276
277         if( strchr( p, '&' ) )
278         {
279             i_len = strchr( p, '&' ) - p;
280         }
281         else
282         {
283             /* for POST method */
284             if( strchr( p, '\n' ) )
285             {
286                 i_len = strchr( p, '\n' ) - p;
287                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
288             }
289             else
290             {
291                 i_len = strlen( p );
292             }
293         }
294         i_len = __MIN( i_value_max - 1, i_len );
295         if( i_len > 0 )
296         {
297             strncpy( psz_value, p, i_len );
298             psz_value[i_len] = '\0';
299         }
300         else
301         {
302             strncpy( psz_value, "", i_value_max );
303         }
304     }
305     else
306     {
307         strncpy( psz_value, "", i_value_max );
308     }
309 }
310
311 static void uri_decode_url_encoded( char *psz )
312 {
313     char *dup = strdup( psz );
314     char *p = dup;
315
316     while( *p )
317     {
318         if( *p == '%' )
319         {
320             char val[3];
321             p++;
322             if( !*p )
323             {
324                 break;
325             }
326
327             val[0] = *p++;
328             val[1] = *p++;
329             val[2] = '\0';
330
331             *psz++ = strtol( val, NULL, 16 );
332         }
333         else if( *p == '+' )
334         {
335             *psz++ = ' ';
336             p++;
337         }
338         else
339         {
340             *psz++ = *p++;
341         }
342     }
343     *psz++  ='\0';
344     free( dup );
345 }
346
347 static int httpd_page_interface_get( httpd_file_callback_args_t *p_args,
348                                      uint8_t *p_request, int i_request,
349                                      uint8_t **pp_data, int *pi_data )
350 {
351     intf_thread_t *p_intf = (intf_thread_t *)p_args;
352     playlist_t *p_playlist;
353     int i_ret;
354
355     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
356     if( !p_playlist )
357     {
358         return VLC_EGENERIC;
359     }
360
361     if( i_request > 0)
362     {
363         char action[512];
364
365         uri_extract_value( p_request, "action", action, 512 );
366
367         if( !strcmp( action, "play" ) )
368         {
369             int i_item;
370             uri_extract_value( p_request, "item", action, 512 );
371             i_item = atol( action );
372             playlist_Command( p_playlist, PLAYLIST_GOTO, i_item );
373             msg_Dbg( p_intf, "requested playlist item: %i", i_item );
374         }
375         else if( !strcmp( action, "stop" ) )
376         {
377             playlist_Command( p_playlist, PLAYLIST_STOP, 0 );
378             msg_Dbg( p_intf, "requested playlist stop" );
379         }
380         else if( !strcmp( action, "pause" ) )
381         {
382             playlist_Command( p_playlist, PLAYLIST_PAUSE, 0 );
383             msg_Dbg( p_intf, "requested playlist pause" );
384         }
385         else if( !strcmp( action, "next" ) )
386         {
387             playlist_Command( p_playlist, PLAYLIST_GOTO,
388                               p_playlist->i_index + 1 );
389             msg_Dbg( p_intf, "requested playlist next" );
390         }
391         else if( !strcmp( action, "previous" ) )
392         {
393             playlist_Command( p_playlist, PLAYLIST_GOTO,
394                               p_playlist->i_index - 1 );
395             msg_Dbg( p_intf, "requested playlist previous" );
396         }
397         else if( !strcmp( action, "add" ) )
398         {
399             uri_extract_value( p_request, "mrl", action, 512 );
400             uri_decode_url_encoded( action );
401             playlist_Add( p_playlist, action,
402                           PLAYLIST_APPEND, PLAYLIST_END );
403             msg_Dbg( p_intf, "requested playlist add: %s", action );
404         }
405     }
406
407     i_ret = httpd_page_interface_update( p_intf, p_playlist, pp_data, pi_data, i_request ? VLC_TRUE : VLC_FALSE );
408
409     vlc_object_release( p_playlist );
410
411     return i_ret;
412 }
413
414 static int httpd_page_interface_update( intf_thread_t *p_intf,
415                                         playlist_t *p_playlist,
416                                         uint8_t **pp_data, int *pi_data, vlc_bool_t b_redirect )
417 {
418     int i, i_size = 0;
419     char *p;
420
421     vlc_mutex_lock( &p_playlist->object_lock );
422
423     /*
424      * Count playlist items for memory allocation
425      */
426     for ( i = 0; i < p_playlist->i_size; i++ )
427     {
428         i_size += sizeof("<a href=?action=play&item=?>? - </a><br />\n" );
429         i_size += strlen( p_playlist->pp_items[i]->psz_name );
430     }
431     /* add something for all the static strings below */
432     i_size += 8192;
433
434     p = *pp_data = malloc( i_size );
435
436     p += sprintf( p, "<html>\n" );
437     p += sprintf( p, "<head>\n" );
438     p += sprintf( p, "<title>VLC Media Player</title>\n" );
439     if( b_redirect )
440     {
441         p += sprintf( p, "<meta http-equiv=\"refresh\" content=\"0;URL=/\"\n" );
442     }
443     /* p += sprintf( p, "<link rel=\"shortcut icon\" href=\"http://www.videolan.org/favicon.ico\">\n" ); */
444     p += sprintf( p, "</head>\n" );
445     p += sprintf( p, "<body>\n" );
446     p += sprintf( p, "<h2><center><a href=\"http://www.videolan.org\">"
447                      "VLC Media Player</a> (http interface)</center></h2>\n" );
448
449     /*
450      * Display the controls
451      */
452     p += sprintf( p, "<hr />\n" );
453
454     p += sprintf( p, "<td><form method=\"get\" action=\"\">"
455         "<input type=\"submit\" name=\"action\" value=\"stop\" />"
456         "<input type=\"submit\" name=\"action\" value=\"pause\" />"
457         "<input type=\"submit\" name=\"action\" value=\"previous\" />"
458         "<input type=\"submit\" name=\"action\" value=\"next\" />"
459         "</form></td><br />\n" );
460
461     p += sprintf( p, "<td><form method=\"get\" action=\"\" "
462                      "enctype=\"text/plain\" >"
463         "Media Resource Locator: "
464         "<input type=\"text\" name=\"mrl\" size=\"40\" />"
465         "<input type=\"submit\" name=\"action\" value=\"add\" />"
466         "</form></td>\n" );
467
468     p += sprintf( p, "<hr />\n" );
469
470     /*
471      * Display the playlist items
472      */
473     for ( i = 0; i < p_playlist->i_size; i++ )
474     {
475         if( i == p_playlist->i_index ) p += sprintf( p, "<b>" );
476
477         p += sprintf( p, "<a href=?action=play&item=%i>", i );
478         p += sprintf( p, "%i - %s", i,
479                       p_playlist->pp_items[i]->psz_name );
480         p += sprintf( p, "</a>" );
481
482         if( i == p_playlist->i_index ) p += sprintf( p, "</b>" );
483         p += sprintf( p, "<br />\n" );
484     }
485     if ( i == 0 )
486     {
487         p += sprintf( p, "no entries\n" );
488     }
489
490     p += sprintf( p, "</body>\n" );
491     p += sprintf( p, "</html>\n" );
492
493     *pi_data = strlen( *pp_data ) + 1;
494
495     vlc_mutex_unlock( &p_playlist->object_lock );
496
497     return VLC_SUCCESS;
498 }