]> git.sesse.net Git - vlc/blob - modules/access/dvb/http.c
* modules/access/dvb: Full support for DVB MMI menus via an optional HTTP
[vlc] / modules / access / dvb / http.c
1 /*****************************************************************************
2  * http.c: HTTP interface
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  *
6  * Authors: Christophe Massiot <massiot@via.ecp.fr>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
21  *****************************************************************************/
22
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/input.h>
29
30 #ifdef HAVE_UNISTD_H
31 #   include <unistd.h>
32 #endif
33
34 #include <fcntl.h>
35 #include <sys/types.h>
36
37 #include <errno.h>
38
39 /* Include dvbpsi headers */
40 #ifdef HAVE_DVBPSI_DR_H
41 #   include <dvbpsi/dvbpsi.h>
42 #   include <dvbpsi/descriptor.h>
43 #   include <dvbpsi/pat.h>
44 #   include <dvbpsi/pmt.h>
45 #   include <dvbpsi/dr.h>
46 #   include <dvbpsi/psi.h>
47 #else
48 #   include "dvbpsi.h"
49 #   include "descriptor.h"
50 #   include "tables/pat.h"
51 #   include "tables/pmt.h"
52 #   include "descriptors/dr.h"
53 #   include "psi.h"
54 #endif
55
56 #ifdef ENABLE_HTTPD
57 #   include "vlc_httpd.h"
58 #   include "vlc_acl.h"
59 #endif
60
61 #include "dvb.h"
62
63 #ifdef ENABLE_HTTPD
64 struct httpd_file_sys_t
65 {
66     access_t         *p_access;
67     httpd_file_t     *p_file;
68 };
69
70 static int HttpCallback( httpd_file_sys_t *p_args,
71                          httpd_file_t *p_file,
72                          uint8_t *_p_request,
73                          uint8_t **_pp_data, int *pi_data );
74
75 /*****************************************************************************
76  * HTTPOpen: Start the internal HTTP server
77  *****************************************************************************/
78 int E_(HTTPOpen)( access_t *p_access )
79 {
80 #define FREE( x )                                                           \
81     if ( (x) != NULL )                                                      \
82         free( x );
83
84     access_sys_t *p_sys = p_access->p_sys;
85     char          *psz_address, *psz_cert = NULL, *psz_key = NULL,
86                   *psz_ca = NULL, *psz_crl = NULL, *psz_user = NULL,
87                   *psz_password = NULL, *psz_acl = NULL;
88     int           i_port       = 0;
89     char          psz_tmp[10];
90     vlc_acl_t     *p_acl = NULL;
91     httpd_file_sys_t *f;
92
93     vlc_mutex_init( p_access, &p_sys->httpd_mutex );
94     vlc_cond_init( p_access, &p_sys->httpd_cond );
95     p_sys->b_request_frontend_info = p_sys->b_request_mmi_info = VLC_FALSE;
96     p_sys->i_httpd_timeout = 0;
97
98     psz_address = var_GetString( p_access, "dvb-http-host" );
99     if( psz_address != NULL && *psz_address )
100     {
101         char *psz_parser = strchr( psz_address, ':' );
102         if( psz_parser )
103         {
104             *psz_parser++ = '\0';
105             i_port = atoi( psz_parser );
106         }
107     }
108     else
109     {
110         if ( psz_address != NULL ) free( psz_address );
111         return VLC_SUCCESS;
112     }
113
114     /* determine SSL configuration */
115     psz_cert = var_GetString( p_access, "dvb-http-intf-cert" );
116     if ( psz_cert != NULL && *psz_cert )
117     {
118         msg_Dbg( p_access, "enabling TLS for HTTP interface (cert file: %s)",
119                  psz_cert );
120         psz_key = config_GetPsz( p_access, "dvb-http-intf-key" );
121         psz_ca = config_GetPsz( p_access, "dvb-http-intf-ca" );
122         psz_crl = config_GetPsz( p_access, "dvb-http-intf-crl" );
123
124         if ( i_port <= 0 )
125             i_port = 8443;
126     }
127     else
128     {
129         if ( !*psz_cert )
130         {
131             free( psz_cert );
132             psz_cert = NULL;
133         }
134         if ( i_port <= 0 )
135             i_port= 8082;
136     }
137
138     /* Ugly hack to allow to run several HTTP servers on different ports. */
139     sprintf( psz_tmp, ":%d", i_port + 1 );
140     config_PutPsz( p_access, "dvb-http-host", psz_tmp );
141
142     msg_Dbg( p_access, "base %s:%d", psz_address, i_port );
143
144     p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_access), psz_address,
145                                             i_port, psz_cert, psz_key, psz_ca,
146                                             psz_crl );
147     FREE( psz_cert );
148     FREE( psz_key );
149     FREE( psz_ca );
150     FREE( psz_crl );
151
152     if ( p_sys->p_httpd_host == NULL )
153     {
154         msg_Err( p_access, "cannot listen on %s:%d", psz_address, i_port );
155         free( psz_address );
156         return VLC_EGENERIC;
157     }
158     free( psz_address );
159
160     psz_user = var_GetString( p_access, "dvb-http-user" );
161     psz_password = var_GetString( p_access, "dvb-http-password" );
162     psz_acl = var_GetString( p_access, "dvb-http-acl" );
163
164     if ( psz_acl != NULL )
165     {
166         p_acl = ACL_Create( p_access, VLC_FALSE );
167         if( ACL_LoadFile( p_acl, psz_acl ) )
168         {
169             ACL_Destroy( p_acl );
170             p_acl = NULL;
171         }
172     }
173
174     /* Declare an index.html file. */
175     f = malloc( sizeof(httpd_file_sys_t) );
176     f->p_access = p_access;
177     f->p_file = httpd_FileNew( p_sys->p_httpd_host, "/index.html",
178                                "text/html; charset=UTF-8",
179                                psz_user, psz_password, p_acl,
180                                HttpCallback, f );
181
182     FREE( psz_user );
183     FREE( psz_password );
184     FREE( psz_acl );
185     if ( p_acl != NULL )
186         ACL_Destroy( p_acl );
187
188     if ( f->p_file == NULL )
189     {
190         free( f );
191         p_sys->p_httpd_file = NULL;
192         return VLC_EGENERIC;
193     }
194
195     p_sys->p_httpd_file = f;
196     p_sys->p_httpd_redir = httpd_RedirectNew( p_sys->p_httpd_host,
197                                               "/index.html", "/" );
198
199 #undef FREE
200
201     return VLC_SUCCESS;
202 }
203
204 /*****************************************************************************
205  * HTTPClose: Stop the internal HTTP server
206  *****************************************************************************/
207 void E_(HTTPClose)( access_t *p_access )
208 {
209     access_sys_t *p_sys = p_access->p_sys;
210
211     if ( p_sys->p_httpd_host != NULL )
212     {
213         if ( p_sys->p_httpd_file != NULL )
214         {
215             /* Unlock the thread if it is stuck in HttpCallback */
216             vlc_mutex_lock( &p_sys->httpd_mutex );
217             if ( p_sys->b_request_frontend_info == VLC_TRUE )
218             {
219                 p_sys->b_request_frontend_info = VLC_FALSE;
220                 p_sys->psz_frontend_info = strdup("");
221             }
222             if ( p_sys->b_request_mmi_info == VLC_TRUE )
223             {
224                 p_sys->b_request_mmi_info = VLC_FALSE;
225                 p_sys->psz_mmi_info = strdup("");
226             }
227             vlc_cond_signal( &p_sys->httpd_cond );
228             vlc_mutex_unlock( &p_sys->httpd_mutex );
229
230             httpd_FileDelete( p_sys->p_httpd_file->p_file );
231             httpd_RedirectDelete( p_sys->p_httpd_redir );
232         }
233
234         httpd_HostDelete( p_sys->p_httpd_host );
235     }
236
237     vlc_mutex_destroy( &p_sys->httpd_mutex );
238     vlc_cond_destroy( &p_sys->httpd_cond );
239 }
240
241
242 static const char *psz_constant_header =
243     "<html>\n"
244     "<head><title>VLC DVB monitoring interface</title></head>\n"
245     "<body><a href=\"index.html\">Reload this page</a>\n"
246     "<h1>CAM info</h1>\n";
247
248 static const char *psz_constant_middle =
249     "<hr><h1>Frontend Info</h1>\n";
250
251 static const char *psz_constant_footer =
252     "</body></html>\n";
253
254 /****************************************************************************
255  * HttpCallback: Return the index.html file
256  ****************************************************************************/
257 static int HttpCallback( httpd_file_sys_t *p_args,
258                          httpd_file_t *p_file,
259                          uint8_t *_psz_request,
260                          uint8_t **_pp_data, int *pi_data )
261 {
262     access_sys_t *p_sys = p_args->p_access->p_sys;
263     char *psz_request = (char *)_psz_request;
264     char **pp_data = (char **)_pp_data;
265
266     vlc_mutex_lock( &p_sys->httpd_mutex );
267
268     p_sys->i_httpd_timeout = mdate() + I64C(3000000); /* 3 s */
269     p_sys->psz_request = psz_request;
270     p_sys->b_request_frontend_info = VLC_TRUE;
271     if ( p_sys->i_ca_handle )
272     {
273         p_sys->b_request_mmi_info = VLC_TRUE;
274     }
275     else
276     {
277         p_sys->psz_mmi_info = strdup( "No available CAM interface\n" );
278     }
279
280     do
281     {
282         vlc_cond_wait( &p_sys->httpd_cond, &p_sys->httpd_mutex );
283     }
284     while ( p_sys->b_request_frontend_info || p_sys->b_request_mmi_info );
285
286     p_sys->i_httpd_timeout = 0;
287     vlc_mutex_unlock( &p_sys->httpd_mutex );
288
289     *pi_data = strlen( psz_constant_header )
290                 + strlen( p_sys->psz_mmi_info )
291                 + strlen( psz_constant_middle )
292                 + strlen( p_sys->psz_frontend_info )
293                 + strlen( psz_constant_footer ) + 1;
294     *pp_data = malloc( *pi_data );
295
296     sprintf( *pp_data, "%s%s%s%s%s", psz_constant_header,
297              p_sys->psz_mmi_info, psz_constant_middle,
298              p_sys->psz_frontend_info, psz_constant_footer );
299     free( p_sys->psz_frontend_info );
300     free( p_sys->psz_mmi_info );
301
302     return VLC_SUCCESS;
303 }
304
305 /****************************************************************************
306  * HTTPExtractValue: Extract a GET variable from psz_request
307  ****************************************************************************/
308 char *E_(HTTPExtractValue)( char *psz_uri, const char *psz_name,
309                             char *psz_value, int i_value_max )
310 {
311     char *p = psz_uri;
312
313     while( (p = strstr( p, psz_name )) )
314     {
315         /* Verify that we are dealing with a post/get argument */
316         if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
317               && p[strlen(psz_name)] == '=' )
318             break;
319         p++;
320     }
321
322     if( p )
323     {
324         int i_len;
325
326         p += strlen( psz_name );
327         if( *p == '=' ) p++;
328
329         if( strchr( p, '&' ) )
330         {
331             i_len = strchr( p, '&' ) - p;
332         }
333         else
334         {
335             /* for POST method */
336             if( strchr( p, '\n' ) )
337             {
338                 i_len = strchr( p, '\n' ) - p;
339                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
340             }
341             else
342             {
343                 i_len = strlen( p );
344             }
345         }
346         i_len = __MIN( i_value_max - 1, i_len );
347         if( i_len > 0 )
348         {
349             strncpy( psz_value, p, i_len );
350             psz_value[i_len] = '\0';
351         }
352         else
353         {
354             strncpy( psz_value, "", i_value_max );
355         }
356         p += i_len;
357     }
358     else
359     {
360         strncpy( psz_value, "", i_value_max );
361     }
362
363     return p;
364 }
365
366 #endif /* ENABLE_HTTPD */