Fix HTTP DVB ACL
[vlc.git] / 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc_access.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     access_sys_t *p_sys = p_access->p_sys;
81     char          *psz_address, *psz_cert = NULL, *psz_key = NULL,
82                   *psz_ca = NULL, *psz_crl = NULL, *psz_user = NULL,
83                   *psz_password = NULL, *psz_acl = NULL;
84     int           i_port       = 0;
85     char          psz_tmp[10];
86     vlc_acl_t     *p_acl = NULL;
87     httpd_file_sys_t *f;
88
89     vlc_mutex_init( p_access, &p_sys->httpd_mutex );
90     vlc_cond_init( p_access, &p_sys->httpd_cond );
91     p_sys->b_request_frontend_info = p_sys->b_request_mmi_info = VLC_FALSE;
92     p_sys->i_httpd_timeout = 0;
93
94     psz_address = var_GetNonEmptyString( p_access, "dvb-http-host" );
95     if( psz_address != NULL )
96     {
97         char *psz_parser = strchr( psz_address, ':' );
98         if( psz_parser )
99         {
100             *psz_parser++ = '\0';
101             i_port = atoi( psz_parser );
102         }
103     }
104     else
105         return VLC_SUCCESS;
106
107     /* determine SSL configuration */
108     psz_cert = var_GetNonEmptyString( p_access, "dvb-http-intf-cert" );
109     if ( psz_cert != NULL )
110     {
111         msg_Dbg( p_access, "enabling TLS for HTTP interface (cert file: %s)",
112                  psz_cert );
113         psz_key = config_GetPsz( p_access, "dvb-http-intf-key" );
114         psz_ca = config_GetPsz( p_access, "dvb-http-intf-ca" );
115         psz_crl = config_GetPsz( p_access, "dvb-http-intf-crl" );
116
117         if ( i_port <= 0 )
118             i_port = 8443;
119     }
120     else
121     {
122         if ( i_port <= 0 )
123             i_port= 8082;
124     }
125
126     /* Ugly hack to allow to run several HTTP servers on different ports. */
127     sprintf( psz_tmp, ":%d", i_port + 1 );
128     config_PutPsz( p_access, "dvb-http-host", psz_tmp );
129
130     msg_Dbg( p_access, "base %s:%d", psz_address, i_port );
131
132     p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_access), psz_address,
133                                             i_port, psz_cert, psz_key, psz_ca,
134                                             psz_crl );
135     free( psz_cert );
136     free( psz_key );
137     free( psz_ca );
138     free( psz_crl );
139
140     if ( p_sys->p_httpd_host == NULL )
141     {
142         msg_Err( p_access, "cannot listen on %s:%d", psz_address, i_port );
143         free( psz_address );
144         return VLC_EGENERIC;
145     }
146     free( psz_address );
147
148     psz_user = var_GetNonEmptyString( p_access, "dvb-http-user" );
149     psz_password = var_GetNonEmptyString( p_access, "dvb-http-password" );
150     psz_acl = var_GetNonEmptyString( p_access, "dvb-http-acl" );
151
152     if ( psz_acl != NULL )
153     {
154         p_acl = ACL_Create( p_access, VLC_FALSE );
155         if( ACL_LoadFile( p_acl, psz_acl ) )
156         {
157             ACL_Destroy( p_acl );
158             p_acl = NULL;
159         }
160     }
161
162     /* Declare an index.html file. */
163     f = malloc( sizeof(httpd_file_sys_t) );
164     f->p_access = p_access;
165     f->p_file = httpd_FileNew( p_sys->p_httpd_host, "/index.html",
166                                "text/html; charset=UTF-8",
167                                psz_user, psz_password, p_acl,
168                                HttpCallback, f );
169
170     free( psz_user );
171     free( psz_password );
172     free( psz_acl );
173     if ( p_acl != NULL )
174         ACL_Destroy( p_acl );
175
176     if ( f->p_file == NULL )
177     {
178         free( f );
179         p_sys->p_httpd_file = NULL;
180         return VLC_EGENERIC;
181     }
182
183     p_sys->p_httpd_file = f;
184     p_sys->p_httpd_redir = httpd_RedirectNew( p_sys->p_httpd_host,
185                                               "/index.html", "/" );
186
187     return VLC_SUCCESS;
188 }
189
190 /*****************************************************************************
191  * HTTPClose: Stop the internal HTTP server
192  *****************************************************************************/
193 void E_(HTTPClose)( access_t *p_access )
194 {
195     access_sys_t *p_sys = p_access->p_sys;
196
197     if ( p_sys->p_httpd_host != NULL )
198     {
199         if ( p_sys->p_httpd_file != NULL )
200         {
201             /* Unlock the thread if it is stuck in HttpCallback */
202             vlc_mutex_lock( &p_sys->httpd_mutex );
203             if ( p_sys->b_request_frontend_info == VLC_TRUE )
204             {
205                 p_sys->b_request_frontend_info = VLC_FALSE;
206                 p_sys->psz_frontend_info = strdup("");
207             }
208             if ( p_sys->b_request_mmi_info == VLC_TRUE )
209             {
210                 p_sys->b_request_mmi_info = VLC_FALSE;
211                 p_sys->psz_mmi_info = strdup("");
212             }
213             vlc_cond_signal( &p_sys->httpd_cond );
214             vlc_mutex_unlock( &p_sys->httpd_mutex );
215
216             httpd_FileDelete( p_sys->p_httpd_file->p_file );
217             httpd_RedirectDelete( p_sys->p_httpd_redir );
218         }
219
220         httpd_HostDelete( p_sys->p_httpd_host );
221     }
222
223     vlc_mutex_destroy( &p_sys->httpd_mutex );
224     vlc_cond_destroy( &p_sys->httpd_cond );
225 }
226
227
228 static const char *psz_constant_header =
229     "<html>\n"
230     "<head><title>VLC DVB monitoring interface</title></head>\n"
231     "<body><a href=\"index.html\">Reload this page</a>\n"
232     "<h1>CAM info</h1>\n";
233
234 static const char *psz_constant_middle =
235     "<hr><h1>Frontend Info</h1>\n";
236
237 static const char *psz_constant_footer =
238     "</body></html>\n";
239
240 /****************************************************************************
241  * HttpCallback: Return the index.html file
242  ****************************************************************************/
243 static int HttpCallback( httpd_file_sys_t *p_args,
244                          httpd_file_t *p_file,
245                          uint8_t *_psz_request,
246                          uint8_t **_pp_data, int *pi_data )
247 {
248     access_sys_t *p_sys = p_args->p_access->p_sys;
249     char *psz_request = (char *)_psz_request;
250     char **pp_data = (char **)_pp_data;
251
252     vlc_mutex_lock( &p_sys->httpd_mutex );
253
254     p_sys->i_httpd_timeout = mdate() + I64C(3000000); /* 3 s */
255     p_sys->psz_request = psz_request;
256     p_sys->b_request_frontend_info = VLC_TRUE;
257     if ( p_sys->i_ca_handle )
258     {
259         p_sys->b_request_mmi_info = VLC_TRUE;
260     }
261     else
262     {
263         p_sys->psz_mmi_info = strdup( "No available CAM interface\n" );
264     }
265
266     do
267     {
268         vlc_cond_wait( &p_sys->httpd_cond, &p_sys->httpd_mutex );
269     }
270     while ( p_sys->b_request_frontend_info || p_sys->b_request_mmi_info );
271
272     p_sys->i_httpd_timeout = 0;
273     vlc_mutex_unlock( &p_sys->httpd_mutex );
274
275     *pi_data = strlen( psz_constant_header )
276                 + strlen( p_sys->psz_mmi_info )
277                 + strlen( psz_constant_middle )
278                 + strlen( p_sys->psz_frontend_info )
279                 + strlen( psz_constant_footer ) + 1;
280     *pp_data = malloc( *pi_data );
281
282     sprintf( *pp_data, "%s%s%s%s%s", psz_constant_header,
283              p_sys->psz_mmi_info, psz_constant_middle,
284              p_sys->psz_frontend_info, psz_constant_footer );
285     free( p_sys->psz_frontend_info );
286     free( p_sys->psz_mmi_info );
287
288     return VLC_SUCCESS;
289 }
290
291 /****************************************************************************
292  * HTTPExtractValue: Extract a GET variable from psz_request
293  ****************************************************************************/
294 char *E_(HTTPExtractValue)( char *psz_uri, const char *psz_name,
295                             char *psz_value, int i_value_max )
296 {
297     char *p = psz_uri;
298
299     while( (p = strstr( p, psz_name )) )
300     {
301         /* Verify that we are dealing with a post/get argument */
302         if( (p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n')
303               && p[strlen(psz_name)] == '=' )
304             break;
305         p++;
306     }
307
308     if( p )
309     {
310         int i_len;
311
312         p += strlen( psz_name );
313         if( *p == '=' ) p++;
314
315         if( strchr( p, '&' ) )
316         {
317             i_len = strchr( p, '&' ) - p;
318         }
319         else
320         {
321             /* for POST method */
322             if( strchr( p, '\n' ) )
323             {
324                 i_len = strchr( p, '\n' ) - p;
325                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
326             }
327             else
328             {
329                 i_len = strlen( p );
330             }
331         }
332         i_len = __MIN( i_value_max - 1, i_len );
333         if( i_len > 0 )
334         {
335             strncpy( psz_value, p, i_len );
336             psz_value[i_len] = '\0';
337         }
338         else
339         {
340             strncpy( psz_value, "", i_value_max );
341         }
342         p += i_len;
343     }
344     else
345     {
346         strncpy( psz_value, "", i_value_max );
347     }
348
349     return p;
350 }
351
352 #endif /* ENABLE_HTTPD */