1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: httpd.c,v 1.1 2003/02/23 19:05:22 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
28 #include <sys/types.h>
39 #elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
43 #if defined( UNDER_CE )
45 #elif defined( WIN32 )
46 # include <winsock2.h>
47 # include <ws2tcpip.h>
49 # define IN_MULTICAST(a) IN_CLASSD(a)
52 # include <netdb.h> /* hostent ... */
53 # include <sys/socket.h>
54 # include <netinet/in.h>
55 # ifdef HAVE_ARPA_INET_H
56 # include <arpa/inet.h> /* inet_ntoa(), inet_aton() */
63 # define INADDR_ANY 0x00000000
66 # define INADDR_NONE 0xFFFFFFFF
69 #define LISTEN_BACKLOG 100
70 #define HTTPD_MAX_CONNECTION 1024
73 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
75 #if defined( WIN32 ) || defined( UNDER_CE )
76 #define SOCKET_CLOSE closesocket;
78 #define SOCKET_CLOSE close
81 /*****************************************************************************
83 *****************************************************************************/
84 static int Open ( vlc_object_t * );
85 static void Close ( vlc_object_t * );
87 /*****************************************************************************
89 *****************************************************************************/
91 set_description( _("HTTP 1.0 daemon") );
92 set_capability( "httpd", 42 );
93 set_callbacks( Open, Close );
96 /*****************************************************************************
98 *****************************************************************************/
99 static httpd_host_t *RegisterHost( httpd_t *, char *, int );
100 static void UnregisterHost( httpd_t *, httpd_host_t * );
102 static httpd_file_t *RegisterFile( httpd_t *,
103 char *psz_file, char *psz_mime,
104 char *psz_user, char *psz_password,
105 httpd_file_callback pf_fill,
106 httpd_file_callback_args_t *p_args );
107 static void UnregisterFile( httpd_t *, httpd_file_t * );
109 //#define httpd_stream_t httpd_file_t
110 static httpd_stream_t *RegisterStream( httpd_t *,
111 char *psz_file, char *psz_mime,
112 char *psz_user, char *psz_password );
113 static int SendStream( httpd_t *, httpd_stream_t *, uint8_t *, int );
114 static void UnregisterStream( httpd_t *, httpd_stream_t* );
116 /*****************************************************************************
117 * Internal definitions
118 *****************************************************************************/
126 struct sockaddr_in sock;
131 #define HTTPD_AUTHENTICATE_NONE 0
132 #define HTTPD_AUTHENTICATE_BASIC 1
134 //typedef httpd_file_t httpd_stream_t;
144 int i_authenticate_method;
145 char *psz_user; /* NULL if no auth */
146 char *psz_password; /* NULL if no auth */
148 vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback
149 true: it's up to the program to give data to httpd */
150 void *p_sys; /* provided for user */
151 httpd_file_callback pf_fill; /* it should allocate and fill *pp_data and *pi_data */
154 int i_buffer_size; /* buffer size */
155 uint8_t *p_buffer; /* buffer */
156 int i_buffer; /* reading pointer */
157 int i_buffer_valid; /* valid data from 0 */
162 #define HTTPD_CONNECTION_RECEIVING_REQUEST 1
163 #define HTTPD_CONNECTION_SENDING_HEADER 2
164 #define HTTPD_CONNECTION_SENDING_FILE 3
165 #define HTTPD_CONNECTION_SENDING_STREAM 4
167 typedef struct httpd_connection_s
169 struct httpd_connection_s *p_next;
170 struct httpd_connection_s *p_prev;
172 struct sockaddr_in sock;
177 char *psz_file; // file to be send
178 int i_http_error; // error to be send with the file
179 char *psz_user; // if Authorization in the request header
182 httpd_file_t *p_file;
186 int i_buffer; /* private */
187 } httpd_connection_t;
196 vlc_mutex_t host_lock;
197 volatile int i_host_count;
200 vlc_mutex_t file_lock;
204 vlc_mutex_t connection_lock;
205 int i_connection_count;
206 httpd_connection_t *p_first_connection;
209 static void httpd_Thread( httpd_sys_t *p_httpt );
210 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
211 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
213 /*****************************************************************************
215 *****************************************************************************/
217 static int Open( vlc_object_t *p_this )
219 httpd_t *p_httpd = (httpd_t*)p_this;
220 httpd_sys_t *p_httpt;
222 /* Launch httpt thread */
223 if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
225 msg_Err( p_this, "out of memory" );
226 return( VLC_EGENERIC );
232 /* init httpt_t structure */
233 vlc_mutex_init( p_httpd, &p_httpt->host_lock );
234 p_httpt->i_host_count = 0;
235 p_httpt->host = NULL;
237 vlc_mutex_init( p_httpd, &p_httpt->file_lock );
238 p_httpt->i_file_count = 0;
239 p_httpt->file = NULL;
241 vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
242 p_httpt->i_connection_count = 0;
243 p_httpt->p_first_connection = NULL;
245 /* start the thread */
246 if( vlc_thread_create( p_httpt, "httpd thread",
247 httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
249 msg_Err( p_this, "cannot spawn http thread" );
251 vlc_mutex_destroy( &p_httpt->host_lock );
252 vlc_mutex_destroy( &p_httpt->file_lock );
253 vlc_mutex_destroy( &p_httpt->connection_lock );
255 vlc_object_destroy( p_httpt );
256 return( VLC_EGENERIC );
259 msg_Info( p_httpd, "http thread launched" );
261 p_httpd->p_sys = p_httpt;
262 p_httpd->pf_register_host = RegisterHost;
263 p_httpd->pf_unregister_host = UnregisterHost;
264 p_httpd->pf_register_file = RegisterFile;
265 p_httpd->pf_unregister_file = UnregisterFile;
266 p_httpd->pf_register_stream = RegisterStream;
267 p_httpd->pf_send_stream = SendStream;
268 p_httpd->pf_unregister_stream=UnregisterStream;
270 return( VLC_SUCCESS );
273 /*****************************************************************************
274 * Close: close the target
275 *****************************************************************************/
276 static void Close( vlc_object_t * p_this )
278 httpd_t *p_httpd = (httpd_t*)p_this;
279 httpd_sys_t *p_httpt = p_httpd->p_sys;
281 httpd_connection_t *p_con;
285 vlc_thread_join( p_httpt );
287 /* first close all host */
288 vlc_mutex_destroy( &p_httpt->host_lock );
289 if( p_httpt->i_host_count )
291 msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
293 for( i = 0; i < p_httpt->i_host_count; i++ )
295 #define p_host p_httpt->host[i]
296 FREE( p_host->psz_host_addr );
297 SOCKET_CLOSE( p_host->fd );
302 FREE( p_httpt->host );
305 vlc_mutex_destroy( &p_httpt->file_lock );
306 if( p_httpt->i_file_count )
308 msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
310 for( i = 0; i < p_httpt->i_file_count; i++ )
312 #define p_file p_httpt->file[i]
313 FREE( p_file->psz_file );
314 FREE( p_file->psz_mime );
315 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
317 FREE( p_file->psz_user );
318 FREE( p_file->psz_password );
320 FREE( p_file->p_buffer );
325 FREE( p_httpt->file );
327 /* andd close all connection */
328 vlc_mutex_destroy( &p_httpt->connection_lock );
329 if( p_httpt->i_connection_count )
331 msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
333 while( ( p_con = p_httpt->p_first_connection ) )
335 httpd_ConnnectionClose( p_httpt, p_con );
338 msg_Info( p_httpd, "httpd instance closed" );
339 vlc_object_destroy( p_httpt );
343 /****************************************************************************
344 ****************************************************************************
347 ****************************************************************************
348 ****************************************************************************/
349 static int BuildAddr( struct sockaddr_in * p_socket,
350 const char * psz_address, int i_port )
353 memset( p_socket, 0, sizeof( struct sockaddr_in ) );
354 p_socket->sin_family = AF_INET; /* family */
355 p_socket->sin_port = htons( (uint16_t)i_port );
358 p_socket->sin_addr.s_addr = INADDR_ANY;
362 struct hostent * p_hostent;
364 /* Try to convert address directly from in_addr - this will work if
365 * psz_address is dotted decimal. */
366 #ifdef HAVE_ARPA_INET_H
367 if( !inet_aton( psz_address, &p_socket->sin_addr ) )
369 p_socket->sin_addr.s_addr = inet_addr( psz_address );
370 if( p_socket->sin_addr.s_addr == INADDR_NONE )
373 /* We have a fqdn, try to find its address */
374 if ( (p_hostent = gethostbyname( psz_address )) == NULL )
379 /* Copy the first address of the host in the socket address */
380 memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
381 p_hostent->h_length );
389 * listen on a host for a httpd instance
392 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
394 httpd_host_t *p_host;
395 struct sockaddr_in sock;
401 if( BuildAddr( &sock, psz_host_addr, i_port ) )
403 msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
407 /* is it already declared ? */
408 vlc_mutex_lock( &p_httpt->host_lock );
409 for( i = 0; i < p_httpt->i_host_count; i++ )
411 if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
412 p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr )
418 if( i < p_httpt->i_host_count )
420 /* yes, increment ref count and succed */
421 p_httpt->host[i]->i_ref++;
422 vlc_mutex_unlock( &p_httpt->host_lock );
426 /* need to add a new listening socket */
429 fd = socket( AF_INET, SOCK_STREAM, 0 );
432 msg_Err( p_httpt, "cannot open socket" );
437 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
438 (void *) &i_opt, sizeof( i_opt ) ) < 0 )
440 msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
443 if( bind( fd, &sock, sizeof( struct sockaddr_in ) ) < 0 )
445 msg_Err( p_httpt, "cannot bind socket" );
448 /* set to non-blocking */
449 if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
451 msg_Err( p_httpt, "cannot F_GETFL socket" );
454 if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
456 msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
460 if( listen( fd, LISTEN_BACKLOG ) < 0 )
462 msg_Err( p_httpt, "cannot listen socket" );
468 p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) );
472 p_httpt->host = malloc( sizeof( httpd_host_t *) );
474 p_host = malloc( sizeof( httpd_host_t ) );
476 p_host->psz_host_addr = strdup( psz_host_addr );
477 p_host->i_port = i_port;
481 p_httpt->host[p_httpt->i_host_count++] = p_host;
482 vlc_mutex_unlock( &p_httpt->host_lock );
487 vlc_mutex_unlock( &p_httpt->host_lock );
494 static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
496 return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
500 * remove a listening host for an httpd instance
502 static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
506 vlc_mutex_lock( &p_httpt->host_lock );
507 for( i = 0; i < p_httpt->i_host_count; i++ )
509 if( p_httpt->host[i] == p_host )
514 if( i >= p_httpt->i_host_count )
516 vlc_mutex_unlock( &p_httpt->host_lock );
517 msg_Err( p_httpt, "cannot unregister host" );
523 if( p_host->i_ref > 0 )
526 vlc_mutex_unlock( &p_httpt->host_lock );
531 FREE( p_host->psz_host_addr );
532 SOCKET_CLOSE( p_host->fd );
536 if( p_httpt->i_host_count <= 1 )
538 FREE( p_httpt->host );
539 p_httpt->i_host_count = 0;
545 i_move = p_httpt->i_host_count - i - 1;
549 memmove( &p_httpt->host[i],
551 i_move * sizeof( httpd_host_t * ) );
554 p_httpt->i_host_count--;
555 p_httpt->host = realloc( p_httpt->host,
556 p_httpt->i_host_count * sizeof( httpd_host_t * ) );
559 vlc_mutex_unlock( &p_httpt->host_lock );
561 static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
563 _UnregisterHost( p_httpd->p_sys, p_host );
567 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
570 if( p_httpt->i_file_count )
572 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) );
576 p_httpt->file = malloc( sizeof( httpd_file_t *) );
579 p_httpt->file[p_httpt->i_file_count++] = p_file;
582 static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt,
583 char *psz_file, char *psz_mime,
584 char *psz_user, char *psz_password,
585 httpd_file_callback pf_fill,
586 httpd_file_callback_args_t *p_args )
588 httpd_file_t *p_file;
591 vlc_mutex_lock( &p_httpt->file_lock );
592 for( i = 0; i < p_httpt->i_file_count; i++ )
594 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
599 if( i < p_httpt->i_file_count )
601 vlc_mutex_unlock( &p_httpt->file_lock );
602 msg_Err( p_httpt, "%s already registered", psz_file );
606 p_file = malloc( sizeof( httpd_file_t ) );
608 p_file->psz_file = strdup( psz_file );
609 p_file->psz_mime = strdup( psz_mime );
610 if( psz_user && *psz_user )
612 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
613 p_file->psz_user = strdup( psz_user );
614 p_file->psz_password = strdup( psz_password );
618 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
619 p_file->psz_user = NULL;
620 p_file->psz_password = NULL;
623 p_file->b_stream = VLC_FALSE;
624 p_file->p_sys = p_args;
625 p_file->pf_fill = pf_fill;
627 p_file->i_buffer_size = 0;
628 p_file->i_buffer_valid = 0;
629 p_file->i_buffer = 0;
630 p_file->p_buffer = NULL;
632 __RegisterFile( p_httpt, p_file );
634 vlc_mutex_unlock( &p_httpt->file_lock );
638 static httpd_file_t *RegisterFile( httpd_t *p_httpd,
639 char *psz_file, char *psz_mime,
640 char *psz_user, char *psz_password,
641 httpd_file_callback pf_fill,
642 httpd_file_callback_args_t *p_args )
644 return( _RegisterFile( p_httpd->p_sys,
645 psz_file, psz_mime, psz_user, psz_password,
649 static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt,
650 char *psz_file, char *psz_mime,
651 char *psz_user, char *psz_password )
653 httpd_stream_t *p_stream;
656 vlc_mutex_lock( &p_httpt->file_lock );
657 for( i = 0; i < p_httpt->i_file_count; i++ )
659 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
664 if( i < p_httpt->i_file_count )
666 vlc_mutex_unlock( &p_httpt->file_lock );
667 msg_Err( p_httpt, "%s already registeret", psz_file );
671 p_stream = malloc( sizeof( httpd_stream_t ) );
673 p_stream->psz_file = strdup( psz_file );
674 p_stream->psz_mime = strdup( psz_mime );
675 if( psz_user && *psz_user )
677 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
678 p_stream->psz_user = strdup( psz_user );
679 p_stream->psz_password = strdup( psz_password );
683 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
684 p_stream->psz_user = NULL;
685 p_stream->psz_password = NULL;
688 p_stream->b_stream = VLC_TRUE;
689 p_stream->p_sys = NULL;
690 p_stream->pf_fill = NULL;
691 p_stream->i_buffer_size = 1024*1024;
692 p_stream->i_buffer_valid = 0;
693 p_stream->i_buffer = 0;
694 p_stream->p_buffer = malloc( p_stream->i_buffer_size );
696 __RegisterFile( p_httpt, p_stream );
698 vlc_mutex_unlock( &p_httpt->file_lock );
702 static httpd_stream_t *RegisterStream( httpd_t *p_httpd,
703 char *psz_file, char *psz_mime,
704 char *psz_user, char *psz_password )
706 return( _RegisterStream( p_httpd->p_sys,
707 psz_file, psz_mime, psz_user, psz_password ) );
710 static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
714 vlc_mutex_lock( &p_httpt->file_lock );
715 for( i = 0; i < p_httpt->i_file_count; i++ )
717 if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
722 if( i >= p_httpt->i_file_count )
724 vlc_mutex_unlock( &p_httpt->file_lock );
725 msg_Err( p_httpt, "cannot unregister file" );
729 if( p_file->i_ref > 0 )
731 httpd_connection_t *p_con;
732 /* force closing all connection for this file */
733 msg_Err( p_httpt, "closing all client connection" );
735 vlc_mutex_lock( &p_httpt->connection_lock );
736 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
738 httpd_connection_t *p_next;
740 p_next = p_con->p_next;
741 if( p_con->p_file == p_file )
743 httpd_ConnnectionClose( p_httpt, p_con );
747 vlc_mutex_unlock( &p_httpt->connection_lock );
750 FREE( p_file->psz_file );
751 FREE( p_file->psz_mime );
752 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
754 FREE( p_file->psz_user );
755 FREE( p_file->psz_password );
757 FREE( p_file->p_buffer );
762 if( p_httpt->i_file_count == 1 )
764 FREE( p_httpt->file );
765 p_httpt->i_file_count = 0;
771 i_move = p_httpt->i_file_count - i - 1;
774 memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
776 p_httpt->i_file_count--;
777 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
780 vlc_mutex_unlock( &p_httpt->file_lock );
782 static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
784 _UnregisterFile( p_httpd->p_sys, p_file );
787 static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
789 _UnregisterFile( p_httpd->p_sys, p_stream );
794 static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
798 return( VLC_SUCCESS );
801 vlc_mutex_lock( &p_httpt->file_lock );
802 if( p_stream->i_buffer_size < p_stream->i_buffer_valid + i_data )
804 /* not enough room */
805 if( p_stream->i_buffer < p_stream->i_buffer_valid )
807 memmove( p_stream->p_buffer,
808 p_stream->p_buffer + p_stream->i_buffer,
809 p_stream->i_buffer_valid - p_stream->i_buffer );
811 p_stream->i_buffer_valid -= p_stream->i_buffer;
813 p_stream->i_buffer = 0;
815 i_data = __MIN( i_data, p_stream->i_buffer_size - p_stream->i_buffer_valid );
816 memcpy( p_stream->p_buffer + p_stream->i_buffer_valid, p_data, i_data );
817 p_stream->i_buffer_valid += i_data;
819 vlc_mutex_unlock( &p_httpt->file_lock );
821 return( VLC_SUCCESS );
823 static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
825 return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
828 /****************************************************************************/
829 /****************************************************************************/
830 /****************************************************************************/
831 /****************************************************************************/
832 /****************************************************************************/
834 static int httpd_page_401_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
838 p = *pp_data = malloc( 1024 );
840 p += sprintf( p, "<html>\n" );
841 p += sprintf( p, "<head>\n" );
842 p += sprintf( p, "<title>Error 401</title>\n" );
843 p += sprintf( p, "</head>\n" );
844 p += sprintf( p, "<body>\n" );
845 p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
846 p += sprintf( p, "<hr />\n" );
847 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
848 p += sprintf( p, "</body>\n" );
849 p += sprintf( p, "</html>\n" );
851 *pi_data = strlen( *pp_data ) + 1;
855 static int httpd_page_404_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
859 p = *pp_data = malloc( 1024 );
861 p += sprintf( p, "<html>\n" );
862 p += sprintf( p, "<head>\n" );
863 p += sprintf( p, "<title>Error 404</title>\n" );
864 p += sprintf( p, "</head>\n" );
865 p += sprintf( p, "<body>\n" );
866 p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
867 p += sprintf( p, "<hr />\n" );
868 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
869 p += sprintf( p, "</body>\n" );
870 p += sprintf( p, "</html>\n" );
872 *pi_data = strlen( *pp_data ) + 1;
877 static int httpd_page_admin_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
879 httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
880 httpd_connection_t *p_con;
884 /* FIXME FIXME do not use static size FIXME FIXME*/
885 p = *pp_data = malloc( 8096 );
887 p += sprintf( p, "<html>\n" );
888 p += sprintf( p, "<head>\n" );
889 p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
890 p += sprintf( p, "</head>\n" );
891 p += sprintf( p, "<body>\n" );
892 p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
893 p += sprintf( p, "<h2><center>Admin page</center></h2>\n" );
896 vlc_mutex_lock( &p_httpt->host_lock );
897 p += sprintf( p, "<h3>Host list</h3>\n" );
898 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
899 p += sprintf( p, "<tr>\n<th>Host</th><th>Port</th><th>IP</th>\n</tr>\n" );
901 for( i = 0; i < p_httpt->i_host_count; i++ )
903 p += sprintf( p, "<tr>\n" );
904 p += sprintf( p, "<td>%s</td>\n", p_httpt->host[i]->psz_host_addr );
905 p += sprintf( p, "<td>%d</td>\n", p_httpt->host[i]->i_port );
906 p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_httpt->host[i]->sock.sin_addr ) );
907 p += sprintf( p, "</tr>\n" );
909 p += sprintf( p, "</table>\n" );
910 vlc_mutex_unlock( &p_httpt->host_lock );
913 /* XXX do not take lock on file_lock */
914 p += sprintf( p, "<h3>File list</h3>\n" );
915 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
916 p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
918 for( i = 0; i < p_httpt->i_file_count; i++ )
920 if( !p_httpt->file[i]->b_stream )
922 p += sprintf( p, "<tr>\n" );
923 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
924 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
925 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
926 p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
927 p += sprintf( p, "</tr>\n" );
930 p += sprintf( p, "</table>\n" );
933 /* XXX do not take lock on file_lock */
934 p += sprintf( p, "<h3>Stream list</h3>\n" );
935 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
936 p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
938 for( i = 0; i < p_httpt->i_file_count; i++ )
940 if( p_httpt->file[i]->b_stream )
942 p += sprintf( p, "<tr>\n" );
943 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
944 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
945 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
946 p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
947 p += sprintf( p, "</tr>\n" );
950 p += sprintf( p, "</table>\n" );
952 /* connection list */
953 /* XXX do not take lock on connection_lock */
954 p += sprintf( p, "<h3>Connection list</h3>\n" );
955 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
956 p += sprintf( p, "<tr>\n<th>IP</th><th>Requested File</th><th>Status</th>\n</tr>\n" );
958 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
960 p += sprintf( p, "<tr>\n" );
961 p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_con->sock.sin_addr ) );
962 p += sprintf( p, "<td>%s</td>\n", p_con->psz_file );
963 p += sprintf( p, "<td>%d</td>\n", p_con->i_http_error );
964 p += sprintf( p, "</tr>\n" );
966 p += sprintf( p, "</table>\n" );
969 /* www.videolan.org */
970 p += sprintf( p, "<hr />\n" );
971 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
972 p += sprintf( p, "</body>\n" );
973 p += sprintf( p, "</html>\n" );
975 *pi_data = strlen( *pp_data ) + 1;
981 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
983 httpd_connection_t *p_con;
985 msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
987 /* create a new connection and link it */
988 p_con = malloc( sizeof( httpd_connection_t ) );
989 p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST;
991 p_con->sock = *p_sock;
992 p_con->psz_file = NULL;
993 p_con->i_http_error = 0;
994 p_con->psz_user = NULL;
995 p_con->psz_password = NULL;
996 p_con->p_file = NULL;
999 p_con->i_buffer_size = 8096;
1000 p_con->p_buffer = malloc( p_con->i_buffer_size );
1002 p_con->p_next = NULL;
1004 if( p_httpt->p_first_connection )
1006 httpd_connection_t *p_last;
1008 p_last = p_httpt->p_first_connection;
1009 while( p_last->p_next )
1011 p_last = p_last->p_next;
1014 p_last->p_next = p_con;
1015 p_con->p_prev = p_last;
1019 p_con->p_prev = NULL;
1021 p_httpt->p_first_connection = p_con;
1024 p_httpt->i_connection_count++;
1027 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1029 msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1031 p_httpt->i_connection_count--;
1032 /* first cut out from list */
1035 p_con->p_prev->p_next = p_con->p_next;
1039 p_httpt->p_first_connection = p_con->p_next;
1044 p_con->p_next->p_prev = p_con->p_prev;
1047 if( p_con->p_file ) p_con->p_file->i_ref--;
1048 FREE( p_con->psz_file );
1050 FREE( p_con->p_buffer );
1051 SOCKET_CLOSE( p_con->fd );
1054 FREE( p_con->psz_user );
1055 FREE( p_con->psz_password );
1060 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1062 char *p = *pp_buffer;
1065 while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1071 for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1076 word[__MIN( i, i_word_max -1 )] = '\0';
1081 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1085 for( p = *pp_buffer; p < p_end; p++ )
1087 if( p + 1 < p_end && *p == '\n' )
1092 if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1099 return VLC_EGENERIC;
1102 //char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1103 static void b64_decode( char *dest, char *src )
1108 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
1109 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
1110 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
1111 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
1112 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
1113 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
1114 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
1115 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
1116 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
1117 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
1118 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
1119 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
1120 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
1121 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
1122 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
1123 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
1126 for( i_level = 0; *src != '\0' > 0; src++ )
1130 c = b64[(unsigned int)*src];
1143 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1147 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1151 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1160 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1169 char user[512] = "";
1170 char password[512] = "";
1172 //msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1174 p = p_con->p_buffer;
1175 p_end = p + strlen( p ) + 1;
1177 httpd_RequestGetWord( command, 32, &p, p_end );
1178 httpd_RequestGetWord( url, 1024, &p, p_end );
1179 httpd_RequestGetWord( version, 32, &p, p_end );
1180 //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1182 if( strcmp( command, "GET" ) )
1185 p_con->psz_file = strdup( "/501.html" );
1186 p_con->i_http_error = 501;
1190 if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1192 p_con->psz_file = strdup( "/505.html" );
1193 p_con->i_http_error = 505;
1203 if( httpd_RequestNextLine( &p, p_end ) )
1205 //msg_Dbg( p_httpt, "failled new line" );
1208 //msg_Dbg( p_httpt, "new line=%s", p );
1210 httpd_RequestGetWord( header, 1024, &p, p_end );
1212 if( !strcmp( header, "Authorization:" ) )
1216 httpd_RequestGetWord( method, 32, &p, p_end );
1217 if( !strcasecmp( method, "BASIC" ) )
1222 httpd_RequestGetWord( basic, 1024, &p, p_end );
1223 //msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1224 b64_decode( decoded, basic );
1226 //msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1227 if( strchr( decoded, ':' ) )
1229 char *p = strchr( decoded, ':' );
1232 strcpy( user, decoded );
1233 strcpy( password, p );
1239 p_con->psz_file = strdup( url );
1240 p_con->i_http_error = 200;
1243 //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1244 FREE( p_con->p_buffer );
1245 p_con->i_buffer = 0;
1246 p_con->i_buffer_size = 0;
1248 //vlc_mutex_lock( &p_httpt->file_lock );
1251 for( i = 0, p_con->p_file = NULL; i < p_httpt->i_file_count; i++ )
1253 if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1255 p_con->p_file = p_httpt->file[i];
1259 if( !p_con->p_file )
1261 p_con->psz_file = strdup( "/404.html" );
1262 p_con->i_http_error = 404;
1264 /* XXX be sure that "/404.html" exist else ... */
1268 if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1270 if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) )
1272 p_con->psz_file = strdup( "/401.html" );
1273 strcpy( user, p_con->p_file->psz_user );
1274 p_con->i_http_error = 401;
1276 /* XXX do not put password on 404 else ... */
1281 p_con->p_file->i_ref++;
1282 // vlc_mutex_unlock( &p_httpt->file_lock );
1284 switch( p_con->i_http_error )
1291 psz_status = "Authorization Required";
1294 psz_status = "Unknown";
1298 p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1300 p_con->i_buffer_size = 4096;
1301 p_con->i_buffer = 0;
1302 p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1304 p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1305 p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1306 if( p_con->i_http_error == 401 )
1308 p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1310 p += sprintf( p, "\r\n" );
1312 p_con->i_buffer_size = strlen( p_con->p_buffer ) + 1;
1314 //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1316 #define HTTPD_STREAM_PACKET 1300
1317 static void httpd_Thread( httpd_sys_t *p_httpt )
1319 httpd_file_t *p_page_admin;
1320 httpd_file_t *p_page_401;
1321 httpd_file_t *p_page_404;
1323 httpd_connection_t *p_con;
1326 msg_Info( p_httpt, "httpd started" );
1328 p_page_401 = _RegisterFile( p_httpt,
1329 "/401.html", "text/html",
1331 httpd_page_401_fill,
1332 (httpd_file_callback_args_t*)NULL );
1333 p_page_404 = _RegisterFile( p_httpt,
1334 "/404.html", "text/html",
1336 httpd_page_404_fill,
1337 (httpd_file_callback_args_t*)NULL );
1338 p_page_admin = _RegisterFile( p_httpt,
1339 "/admin.html", "text/html",
1341 httpd_page_admin_fill,
1342 (httpd_file_callback_args_t*)p_httpt );
1344 while( !p_httpt->b_die )
1347 if( p_httpt->i_host_count <= 0 )
1349 msleep( 100 * 1000 );
1352 vlc_mutex_lock( &p_httpt->host_lock );
1353 /* accept/refuse new connection */
1354 for( i = 0; i < p_httpt->i_host_count; i++ )
1356 int i_sock_size = sizeof( struct sockaddr_in );
1357 struct sockaddr_in sock;
1360 fd = accept( p_httpt->host[i]->fd, &sock, &i_sock_size );
1363 fcntl( fd, F_SETFL, O_NONBLOCK );
1365 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
1367 msg_Warn( p_httpt, "max connection reached" );
1371 /* create a new connection and link it */
1372 httpd_ConnnectionNew( p_httpt, fd, &sock );
1376 vlc_mutex_unlock( &p_httpt->host_lock );
1378 vlc_mutex_lock( &p_httpt->file_lock );
1379 /* now do work for all connections */
1380 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1382 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1386 i_len = recv( p_con->fd,
1387 p_con->p_buffer + p_con->i_buffer,
1388 p_con->i_buffer_size - p_con->i_buffer, 0 );
1390 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
1393 httpd_connection_t *p_next = p_con->p_next;
1395 httpd_ConnnectionClose( p_httpt, p_con );
1398 else if( i_len > 0 )
1402 p_con->i_buffer += i_len;
1404 ptr = p_con->p_buffer + p_con->i_buffer;
1406 if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
1407 ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
1408 p_con->i_buffer >= p_con->i_buffer_size )
1410 p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
1411 httpd_ConnectionParseRequest( p_httpt, p_con );
1414 p_con = p_con->p_next;
1418 p_con = p_con->p_next;
1420 continue; /* just for clarity */
1422 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
1427 i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
1429 // msg_Warn( p_httpt, "on %d send %d bytes %s", p_con->i_buffer_size, i_len, p_con->p_buffer + p_con->i_buffer );
1431 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
1434 httpd_connection_t *p_next = p_con->p_next;
1436 httpd_ConnnectionClose( p_httpt, p_con );
1439 else if( i_len > 0 )
1441 p_con->i_buffer += i_len;
1443 if( p_con->i_buffer >= p_con->i_buffer_size )
1445 if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
1447 p_con->i_buffer_size = 0;
1448 p_con->i_buffer = 0;
1449 FREE( p_con->p_buffer );
1451 if( !p_con->p_file->b_stream )
1453 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
1454 p_con->p_file->pf_fill( p_con->p_file->p_sys, &p_con->p_buffer, &p_con->i_buffer_size );
1458 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
1460 p_con = p_con->p_next;
1464 httpd_connection_t *p_next = p_con->p_next;
1466 httpd_ConnnectionClose( p_httpt, p_con );
1472 p_con = p_con->p_next;
1477 p_con = p_con->p_next;
1479 continue; /* just for clarity */
1481 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
1483 httpd_file_t *p_file = p_con->p_file;
1486 //msg_Dbg( p_httpt, "buffer=%d buffer_size=%d", p_file->i_buffer, p_file->i_buffer_size );
1487 if( p_file->i_buffer < p_file->i_buffer_valid )
1491 i_write = __MIN( p_file->i_buffer_valid - p_file->i_buffer, HTTPD_STREAM_PACKET );
1492 i_len = send( p_con->fd, p_file->p_buffer + p_file->i_buffer, i_write, 0 );
1494 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
1497 httpd_connection_t *p_next = p_con->p_next;
1499 httpd_ConnnectionClose( p_httpt, p_con );
1504 p_con = p_con->p_next;
1509 p_con = p_con->p_next;
1511 continue; /* just for clarity */
1515 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
1516 p_con = p_con->p_next;
1518 } /* for over connection */
1522 /* update position for stream based file */
1523 for( i = 0; i < p_httpt->i_file_count; i++ )
1525 if( p_httpt->file[i]->b_stream )
1527 p_httpt->file[i]->i_buffer += __MIN( p_httpt->file[i]->i_buffer_valid - p_httpt->file[i]->i_buffer,
1528 HTTPD_STREAM_PACKET );
1529 if( p_httpt->file[i]->i_buffer < p_httpt->file[i]->i_buffer_valid )
1536 vlc_mutex_unlock( &p_httpt->file_lock );
1537 if( b_wait ) msleep( 100 );
1539 msg_Info( p_httpt, "httpd stopped" );
1541 _UnregisterFile( p_httpt, p_page_401 );
1542 _UnregisterFile( p_httpt, p_page_404 );
1543 _UnregisterFile( p_httpt, p_page_admin );