From 84aaa859bccb42e1f0967ff8d3486ad6473a2ac0 Mon Sep 17 00:00:00 2001 From: Laurent Aimar Date: Sun, 23 Feb 2003 19:05:22 +0000 Subject: [PATCH] * modules/access_output/http : http output. * httpd : mini http server (be carefull about security issue...) --- include/httpd.h | 109 +++ modules/access_output/http.c | 254 ++++++ modules/misc/httpd.c | 1545 ++++++++++++++++++++++++++++++++++ 3 files changed, 1908 insertions(+) create mode 100644 include/httpd.h create mode 100644 modules/access_output/http.c create mode 100644 modules/misc/httpd.c diff --git a/include/httpd.h b/include/httpd.h new file mode 100644 index 0000000000..0f13e053dd --- /dev/null +++ b/include/httpd.h @@ -0,0 +1,109 @@ +/***************************************************************************** + * httpd.h + ***************************************************************************** + * Copyright (C) 2001-2003 VideoLAN + * $Id: httpd.h,v 1.1 2003/02/23 19:05:22 fenrir Exp $ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +typedef struct httpd_t httpd_t; + +typedef struct httpd_host_t httpd_host_t; + +typedef struct httpd_file_t httpd_file_t; +//typedef struct httpd_stream_t httpd_stream_t; +typedef httpd_file_t httpd_stream_t; + +typedef struct httpd_file_callback_args_t httpd_file_callback_args_t; +typedef int (*httpd_file_callback)( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data ); + +typedef struct httpd_sys_t httpd_sys_t; + +struct httpd_t +{ + VLC_COMMON_MEMBERS + + module_t *p_module; + httpd_sys_t *p_sys; + + httpd_host_t *(*pf_register_host) ( httpd_t *, char *, int ); + void (*pf_unregister_host) ( httpd_t *, httpd_host_t * ); + + httpd_file_t *(*pf_register_file) ( httpd_t *, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password, + httpd_file_callback pf_fill, + httpd_file_callback_args_t *p_args ); + void (*pf_unregister_file) ( httpd_t *, httpd_file_t * ); + + httpd_stream_t *(*pf_register_stream) ( httpd_t *, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password ); + int (*pf_send_stream) ( httpd_t *, + httpd_stream_t *, + uint8_t *, int ); + void (*pf_unregister_stream) ( httpd_t *, httpd_stream_t * ); +}; + +/* + * httpd_Find: + * + * Return the running httpd instance + * (if none and b_create then a new one is created) + */ +static inline httpd_t* httpd_Find( vlc_object_t *p_this, vlc_bool_t b_create ) +{ + httpd_t *p_httpd = NULL; + + p_httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE ); + if( !p_httpd ) + { + if( !b_create ) + { + return( NULL ); + } + + p_httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ); + if( !p_httpd ) + { + msg_Err( p_this, "out of memory" ); + return( NULL ); + } + + p_httpd->p_module = module_Need( p_httpd, "httpd", "" ); + + if( !p_httpd->p_module ) + { + msg_Err( p_this, "no suitable httpd module" ); + vlc_object_destroy( p_httpd ); + return( NULL ); + } + + vlc_object_yield( p_httpd ); + } + + return( p_httpd ); +} + +static inline void httpd_Release( httpd_t *p_httpd ) +{ + vlc_object_release( p_httpd ); +} + + + diff --git a/modules/access_output/http.c b/modules/access_output/http.c new file mode 100644 index 0000000000..2286219989 --- /dev/null +++ b/modules/access_output/http.c @@ -0,0 +1,254 @@ +/***************************************************************************** + * http.c + ***************************************************************************** + * Copyright (C) 2001-2003 VideoLAN + * $Id: http.c,v 1.1 2003/02/23 19:05:22 fenrir Exp $ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include + +#include +#include +#include + +#include "httpd.h" + +#define FREE( p ) if( p ) { free( p); (p) = NULL; } + +#define DEFAULT_PORT 8080 + +/***************************************************************************** + * Exported prototypes + *****************************************************************************/ +static int Open ( vlc_object_t * ); +static void Close ( vlc_object_t * ); + +static int Write( sout_access_out_t *, sout_buffer_t * ); +static int Seek ( sout_access_out_t *, off_t ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + set_description( _("HTTP stream ouput") ); + set_capability( "sout access", 0 ); + add_shortcut( "http" ); + set_callbacks( Open, Close ); +vlc_module_end(); + +struct sout_access_out_sys_t +{ + httpd_t *p_httpd; + + /* host */ + httpd_host_t *p_httpd_host; + + /* stream */ + httpd_stream_t *p_httpd_stream; +}; + +/***************************************************************************** + * Open: open the file + *****************************************************************************/ +static int Open( vlc_object_t *p_this ) +{ + sout_access_out_t *p_access = (sout_access_out_t*)p_this; + sout_access_out_sys_t *p_sys; + + char *psz_parser, *psz_name; + + char *psz_bind_addr; + int i_bind_port; + char *psz_file_name; + + if( !( p_sys = p_access->p_sys = + malloc( sizeof( sout_access_out_sys_t ) ) ) ) + { + msg_Err( p_access, "Not enough memory" ); + return( VLC_EGENERIC ); + } + + /* *** parse p_access->psz_name to extract bind address, port and file name *** */ + /* p_access->psz_name host.name:port/filename */ + psz_name = psz_parser = strdup( p_access->psz_name ); + + psz_bind_addr = psz_parser; + i_bind_port = 0; + psz_file_name = ""; + + while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' ) + { + psz_parser++; + } + if( *psz_parser == ':' ) + { + *psz_parser = '\0'; + psz_parser++; + i_bind_port = atoi( psz_parser ); + + while( *psz_parser && *psz_parser != '/' ) + { + psz_parser++; + } + } + if( *psz_parser == '/' ) + { + *psz_parser = '\0'; + psz_parser++; + psz_file_name = psz_parser; + } + + if( i_bind_port <= 0 ) + { + i_bind_port = DEFAULT_PORT; + } + + if( !*psz_file_name ) + { + psz_file_name = strdup( "/" ); + } + else if( *psz_file_name != '/' ) + { + char *p = psz_file_name; + + psz_file_name = malloc( strlen( p ) + 2 ); + strcpy( psz_file_name, "/" ); + strcat( psz_file_name, p ); + } + + p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_access), VLC_TRUE ); + if( !p_sys->p_httpd ) + { + msg_Err( p_access, "cannot start httpd daemon" ); + + free( psz_name ); + free( psz_file_name ); + free( p_access ); + return( VLC_EGENERIC ); + } + + p_sys->p_httpd_host = + p_sys->p_httpd->pf_register_host( p_sys->p_httpd, + psz_bind_addr, i_bind_port ); + + if( !p_sys->p_httpd_host ) + { + msg_Err( p_access, "cannot listen on %s:%d", psz_bind_addr, i_bind_port ); + httpd_Release( p_sys->p_httpd ); + + free( psz_name ); + free( psz_file_name ); + free( p_access ); + return( VLC_EGENERIC ); + } + + p_sys->p_httpd_stream = + p_sys->p_httpd->pf_register_stream( p_sys->p_httpd, + psz_file_name, "application/x-octet_stream", + NULL, NULL ); + + if( !p_sys->p_httpd_stream ) + { + msg_Err( p_access, "cannot add stream %s", psz_file_name ); + p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd, p_sys->p_httpd_host ); + httpd_Release( p_sys->p_httpd ); + + free( psz_name ); + free( psz_file_name ); + free( p_access ); + + return( VLC_EGENERIC ); + } + + p_access->pf_write = Write; + p_access->pf_seek = Seek; + + return VLC_SUCCESS; +} + +/***************************************************************************** + * Close: close the target + *****************************************************************************/ +static void Close( vlc_object_t * p_this ) +{ + sout_access_out_t *p_access = (sout_access_out_t*)p_this; + sout_access_out_sys_t *p_sys = p_access->p_sys; + + p_sys->p_httpd->pf_unregister_stream( p_sys->p_httpd, p_sys->p_httpd_stream ); + p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd, p_sys->p_httpd_host ); + + httpd_Release( p_sys->p_httpd ); + + msg_Info( p_access, "Close" ); + + free( p_sys ); +} + +/***************************************************************************** + * Write: + *****************************************************************************/ +static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer ) +{ + sout_access_out_sys_t *p_sys = p_access->p_sys; + int i_err = 0; + + while( p_buffer ) + { + sout_buffer_t *p_next; + + i_err = p_sys->p_httpd->pf_send_stream( p_sys->p_httpd, p_sys->p_httpd_stream, + p_buffer->p_buffer, p_buffer->i_size ); + + p_next = p_buffer->p_next; + sout_BufferDelete( p_access->p_sout, p_buffer ); + p_buffer = p_next; + + if( i_err < 0 ) + { + break; + } + } + if( i_err < 0 ) + { + sout_buffer_t *p_next; + while( p_buffer ) + { + p_next = p_buffer->p_next; + sout_BufferDelete( p_access->p_sout, p_buffer ); + p_buffer = p_next; + } + } + + return( i_err < 0 ? VLC_EGENERIC : VLC_SUCCESS ); +} + +/***************************************************************************** + * Seek: seek to a specific location in a file + *****************************************************************************/ +static int Seek( sout_access_out_t *p_access, off_t i_pos ) +{ + msg_Err( p_access, "http sout access cannot seek" ); + return( VLC_EGENERIC ); +} + diff --git a/modules/misc/httpd.c b/modules/misc/httpd.c new file mode 100644 index 0000000000..6bd9a63b99 --- /dev/null +++ b/modules/misc/httpd.c @@ -0,0 +1,1545 @@ +/***************************************************************************** + * httpd.c + ***************************************************************************** + * Copyright (C) 2001-2003 VideoLAN + * $Id: httpd.c,v 1.1 2003/02/23 19:05:22 fenrir Exp $ + * + * Authors: Laurent Aimar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include +#include "httpd.h" + +#ifdef HAVE_UNISTD_H +# include +#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE ) +# include +#endif + +#if defined( UNDER_CE ) +# include +#elif defined( WIN32 ) +# include +# include +# ifndef IN_MULTICAST +# define IN_MULTICAST(a) IN_CLASSD(a) +# endif +#else +# include /* hostent ... */ +# include +# include +# ifdef HAVE_ARPA_INET_H +# include /* inet_ntoa(), inet_aton() */ +# endif +#endif + +#include "network.h" + +#ifndef INADDR_ANY +# define INADDR_ANY 0x00000000 +#endif +#ifndef INADDR_NONE +# define INADDR_NONE 0xFFFFFFFF +#endif + +#define LISTEN_BACKLOG 100 +#define HTTPD_MAX_CONNECTION 1024 + + +#define FREE( p ) if( p ) { free( p); (p) = NULL; } + +#if defined( WIN32 ) || defined( UNDER_CE ) +#define SOCKET_CLOSE closesocket; +#else +#define SOCKET_CLOSE close +#endif + +/***************************************************************************** + * Exported prototypes + *****************************************************************************/ +static int Open ( vlc_object_t * ); +static void Close ( vlc_object_t * ); + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin(); + set_description( _("HTTP 1.0 daemon") ); + set_capability( "httpd", 42 ); + set_callbacks( Open, Close ); +vlc_module_end(); + +/***************************************************************************** + * Prototypes + *****************************************************************************/ +static httpd_host_t *RegisterHost( httpd_t *, char *, int ); +static void UnregisterHost( httpd_t *, httpd_host_t * ); + +static httpd_file_t *RegisterFile( httpd_t *, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password, + httpd_file_callback pf_fill, + httpd_file_callback_args_t *p_args ); +static void UnregisterFile( httpd_t *, httpd_file_t * ); + +//#define httpd_stream_t httpd_file_t +static httpd_stream_t *RegisterStream( httpd_t *, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password ); +static int SendStream( httpd_t *, httpd_stream_t *, uint8_t *, int ); +static void UnregisterStream( httpd_t *, httpd_stream_t* ); + +/***************************************************************************** + * Internal definitions + *****************************************************************************/ +struct httpd_host_t +{ + int i_ref; + + char *psz_host_addr; + int i_port; + + struct sockaddr_in sock; + int fd; + +}; + +#define HTTPD_AUTHENTICATE_NONE 0 +#define HTTPD_AUTHENTICATE_BASIC 1 + +//typedef httpd_file_t httpd_stream_t; + +struct httpd_file_t +{ + int i_ref; + + char *psz_file; + char *psz_mime; + + + int i_authenticate_method; + char *psz_user; /* NULL if no auth */ + char *psz_password; /* NULL if no auth */ + + vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback + true: it's up to the program to give data to httpd */ + void *p_sys; /* provided for user */ + httpd_file_callback pf_fill; /* it should allocate and fill *pp_data and *pi_data */ + + /* private */ + int i_buffer_size; /* buffer size */ + uint8_t *p_buffer; /* buffer */ + int i_buffer; /* reading pointer */ + int i_buffer_valid; /* valid data from 0 */ + +}; + + +#define HTTPD_CONNECTION_RECEIVING_REQUEST 1 +#define HTTPD_CONNECTION_SENDING_HEADER 2 +#define HTTPD_CONNECTION_SENDING_FILE 3 +#define HTTPD_CONNECTION_SENDING_STREAM 4 + +typedef struct httpd_connection_s +{ + struct httpd_connection_s *p_next; + struct httpd_connection_s *p_prev; + + struct sockaddr_in sock; + int fd; + + int i_state; + + char *psz_file; // file to be send + int i_http_error; // error to be send with the file + char *psz_user; // if Authorization in the request header + char *psz_password; + + httpd_file_t *p_file; + + int i_buffer_size; + uint8_t *p_buffer; + int i_buffer; /* private */ +} httpd_connection_t; + +/* + * The httpd thread + */ +struct httpd_sys_t +{ + VLC_COMMON_MEMBERS + + vlc_mutex_t host_lock; + volatile int i_host_count; + httpd_host_t **host; + + vlc_mutex_t file_lock; + int i_file_count; + httpd_file_t **file; + + vlc_mutex_t connection_lock; + int i_connection_count; + httpd_connection_t *p_first_connection; +}; + +static void httpd_Thread( httpd_sys_t *p_httpt ); +static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * ); +static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * ); + +/***************************************************************************** + * Open: + *****************************************************************************/ + +static int Open( vlc_object_t *p_this ) +{ + httpd_t *p_httpd = (httpd_t*)p_this; + httpd_sys_t *p_httpt; + + /* Launch httpt thread */ + if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) ) + { + msg_Err( p_this, "out of memory" ); + return( VLC_EGENERIC ); + } + + p_httpt->b_die = 0; + p_httpt->b_error= 0; + + /* init httpt_t structure */ + vlc_mutex_init( p_httpd, &p_httpt->host_lock ); + p_httpt->i_host_count = 0; + p_httpt->host = NULL; + + vlc_mutex_init( p_httpd, &p_httpt->file_lock ); + p_httpt->i_file_count = 0; + p_httpt->file = NULL; + + vlc_mutex_init( p_httpd, &p_httpt->connection_lock ); + p_httpt->i_connection_count = 0; + p_httpt->p_first_connection = NULL; + + /* start the thread */ + if( vlc_thread_create( p_httpt, "httpd thread", + httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) ) + { + msg_Err( p_this, "cannot spawn http thread" ); + + vlc_mutex_destroy( &p_httpt->host_lock ); + vlc_mutex_destroy( &p_httpt->file_lock ); + vlc_mutex_destroy( &p_httpt->connection_lock ); + + vlc_object_destroy( p_httpt ); + return( VLC_EGENERIC ); + } + + msg_Info( p_httpd, "http thread launched" ); + + p_httpd->p_sys = p_httpt; + p_httpd->pf_register_host = RegisterHost; + p_httpd->pf_unregister_host = UnregisterHost; + p_httpd->pf_register_file = RegisterFile; + p_httpd->pf_unregister_file = UnregisterFile; + p_httpd->pf_register_stream = RegisterStream; + p_httpd->pf_send_stream = SendStream; + p_httpd->pf_unregister_stream=UnregisterStream; + + return( VLC_SUCCESS ); +} + +/***************************************************************************** + * Close: close the target + *****************************************************************************/ +static void Close( vlc_object_t * p_this ) +{ + httpd_t *p_httpd = (httpd_t*)p_this; + httpd_sys_t *p_httpt = p_httpd->p_sys; + + httpd_connection_t *p_con; + int i; + + p_httpt->b_die = 1; + vlc_thread_join( p_httpt ); + + /* first close all host */ + vlc_mutex_destroy( &p_httpt->host_lock ); + if( p_httpt->i_host_count ) + { + msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count ); + } + for( i = 0; i < p_httpt->i_host_count; i++ ) + { +#define p_host p_httpt->host[i] + FREE( p_host->psz_host_addr ); + SOCKET_CLOSE( p_host->fd ); + + FREE( p_host ); +#undef p_host + } + FREE( p_httpt->host ); + + /* now all file */ + vlc_mutex_destroy( &p_httpt->file_lock ); + if( p_httpt->i_file_count ) + { + msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count ); + } + for( i = 0; i < p_httpt->i_file_count; i++ ) + { +#define p_file p_httpt->file[i] + FREE( p_file->psz_file ); + FREE( p_file->psz_mime ); + if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE ) + { + FREE( p_file->psz_user ); + FREE( p_file->psz_password ); + } + FREE( p_file->p_buffer ); + + FREE( p_file ); +#undef p_file + } + FREE( p_httpt->file ); + + /* andd close all connection */ + vlc_mutex_destroy( &p_httpt->connection_lock ); + if( p_httpt->i_connection_count ) + { + msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count ); + } + while( ( p_con = p_httpt->p_first_connection ) ) + { + httpd_ConnnectionClose( p_httpt, p_con ); + } + + msg_Info( p_httpd, "httpd instance closed" ); + vlc_object_destroy( p_httpt ); +} + + +/**************************************************************************** + **************************************************************************** + *** + *** + **************************************************************************** + ****************************************************************************/ +static int BuildAddr( struct sockaddr_in * p_socket, + const char * psz_address, int i_port ) +{ + /* Reset struct */ + memset( p_socket, 0, sizeof( struct sockaddr_in ) ); + p_socket->sin_family = AF_INET; /* family */ + p_socket->sin_port = htons( (uint16_t)i_port ); + if( !*psz_address ) + { + p_socket->sin_addr.s_addr = INADDR_ANY; + } + else + { + struct hostent * p_hostent; + + /* Try to convert address directly from in_addr - this will work if + * psz_address is dotted decimal. */ +#ifdef HAVE_ARPA_INET_H + if( !inet_aton( psz_address, &p_socket->sin_addr ) ) +#else + p_socket->sin_addr.s_addr = inet_addr( psz_address ); + if( p_socket->sin_addr.s_addr == INADDR_NONE ) +#endif + { + /* We have a fqdn, try to find its address */ + if ( (p_hostent = gethostbyname( psz_address )) == NULL ) + { + return( -1 ); + } + + /* Copy the first address of the host in the socket address */ + memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0], + p_hostent->h_length ); + } + } + return( 0 ); +} + + +/* + * listen on a host for a httpd instance + */ + +static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port ) +{ + httpd_host_t *p_host; + struct sockaddr_in sock; + int i; + int fd = -1; + int i_opt; + int i_flags; + + if( BuildAddr( &sock, psz_host_addr, i_port ) ) + { + msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port ); + return NULL; + } + + /* is it already declared ? */ + vlc_mutex_lock( &p_httpt->host_lock ); + for( i = 0; i < p_httpt->i_host_count; i++ ) + { + if( p_httpt->host[i]->sock.sin_port == sock.sin_port && + p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr ) + { + break; + } + } + + if( i < p_httpt->i_host_count ) + { + /* yes, increment ref count and succed */ + p_httpt->host[i]->i_ref++; + vlc_mutex_unlock( &p_httpt->host_lock ); + return NULL; + } + + /* need to add a new listening socket */ + + /* open socket */ + fd = socket( AF_INET, SOCK_STREAM, 0 ); + if( fd < 0 ) + { + msg_Err( p_httpt, "cannot open socket" ); + goto socket_failed; + } + /* reuse socket */ + i_opt = 1; + if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &i_opt, sizeof( i_opt ) ) < 0 ) + { + msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" ); + } + /* bind it */ + if( bind( fd, &sock, sizeof( struct sockaddr_in ) ) < 0 ) + { + msg_Err( p_httpt, "cannot bind socket" ); + goto socket_failed; + } + /* set to non-blocking */ + if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 ) + { + msg_Err( p_httpt, "cannot F_GETFL socket" ); + goto socket_failed; + } + if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 ) + { + msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" ); + goto socket_failed; + } + /* listen */ + if( listen( fd, LISTEN_BACKLOG ) < 0 ) + { + msg_Err( p_httpt, "cannot listen socket" ); + goto socket_failed; + } + + if( p_httpt->host ) + { + p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) ); + } + else + { + p_httpt->host = malloc( sizeof( httpd_host_t *) ); + } + p_host = malloc( sizeof( httpd_host_t ) ); + p_host->i_ref = 1; + p_host->psz_host_addr = strdup( psz_host_addr ); + p_host->i_port = i_port; + p_host->sock = sock; + p_host->fd = fd; + + p_httpt->host[p_httpt->i_host_count++] = p_host; + vlc_mutex_unlock( &p_httpt->host_lock ); + + return p_host; + +socket_failed: + vlc_mutex_unlock( &p_httpt->host_lock ); + if( fd >= 0 ) + { + SOCKET_CLOSE( fd ); + } + return NULL; +} +static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port ) +{ + return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) ); +} + +/* + * remove a listening host for an httpd instance + */ +static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host ) +{ + int i; + + vlc_mutex_lock( &p_httpt->host_lock ); + for( i = 0; i < p_httpt->i_host_count; i++ ) + { + if( p_httpt->host[i] == p_host ) + { + break; + } + } + if( i >= p_httpt->i_host_count ) + { + vlc_mutex_unlock( &p_httpt->host_lock ); + msg_Err( p_httpt, "cannot unregister host" ); + return; + } + + p_host->i_ref--; + + if( p_host->i_ref > 0 ) + { + /* still in use */ + vlc_mutex_unlock( &p_httpt->host_lock ); + return; + } + + /* no more used */ + FREE( p_host->psz_host_addr ); + SOCKET_CLOSE( p_host->fd ); + + FREE( p_host ); + + if( p_httpt->i_host_count <= 1 ) + { + FREE( p_httpt->host ); + p_httpt->i_host_count = 0; + } + else + { + int i_move; + + i_move = p_httpt->i_host_count - i - 1; + + if( i_move > 0 ) + { + memmove( &p_httpt->host[i], + &p_httpt->host[i+1], + i_move * sizeof( httpd_host_t * ) ); + } + + p_httpt->i_host_count--; + p_httpt->host = realloc( p_httpt->host, + p_httpt->i_host_count * sizeof( httpd_host_t * ) ); + } + + vlc_mutex_unlock( &p_httpt->host_lock ); +} +static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host ) +{ + _UnregisterHost( p_httpd->p_sys, p_host ); +} + + +static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file ) +{ + /* add a new file */ + if( p_httpt->i_file_count ) + { + p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) ); + } + else + { + p_httpt->file = malloc( sizeof( httpd_file_t *) ); + } + + p_httpt->file[p_httpt->i_file_count++] = p_file; +} + +static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password, + httpd_file_callback pf_fill, + httpd_file_callback_args_t *p_args ) +{ + httpd_file_t *p_file; + int i; + + vlc_mutex_lock( &p_httpt->file_lock ); + for( i = 0; i < p_httpt->i_file_count; i++ ) + { + if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) ) + { + break; + } + } + if( i < p_httpt->i_file_count ) + { + vlc_mutex_unlock( &p_httpt->file_lock ); + msg_Err( p_httpt, "%s already registered", psz_file ); + return NULL; + } + + p_file = malloc( sizeof( httpd_file_t ) ); + p_file->i_ref = 0; + p_file->psz_file = strdup( psz_file ); + p_file->psz_mime = strdup( psz_mime ); + if( psz_user && *psz_user ) + { + p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC; + p_file->psz_user = strdup( psz_user ); + p_file->psz_password = strdup( psz_password ); + } + else + { + p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE; + p_file->psz_user = NULL; + p_file->psz_password = NULL; + } + + p_file->b_stream = VLC_FALSE; + p_file->p_sys = p_args; + p_file->pf_fill = pf_fill; + + p_file->i_buffer_size = 0; + p_file->i_buffer_valid = 0; + p_file->i_buffer = 0; + p_file->p_buffer = NULL; + + __RegisterFile( p_httpt, p_file ); + + vlc_mutex_unlock( &p_httpt->file_lock ); + + return p_file; +} +static httpd_file_t *RegisterFile( httpd_t *p_httpd, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password, + httpd_file_callback pf_fill, + httpd_file_callback_args_t *p_args ) +{ + return( _RegisterFile( p_httpd->p_sys, + psz_file, psz_mime, psz_user, psz_password, + pf_fill, p_args ) ); +} + +static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password ) +{ + httpd_stream_t *p_stream; + int i; + + vlc_mutex_lock( &p_httpt->file_lock ); + for( i = 0; i < p_httpt->i_file_count; i++ ) + { + if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) ) + { + break; + } + } + if( i < p_httpt->i_file_count ) + { + vlc_mutex_unlock( &p_httpt->file_lock ); + msg_Err( p_httpt, "%s already registeret", psz_file ); + return NULL; + } + + p_stream = malloc( sizeof( httpd_stream_t ) ); + p_stream->i_ref = 0; + p_stream->psz_file = strdup( psz_file ); + p_stream->psz_mime = strdup( psz_mime ); + if( psz_user && *psz_user ) + { + p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC; + p_stream->psz_user = strdup( psz_user ); + p_stream->psz_password = strdup( psz_password ); + } + else + { + p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE; + p_stream->psz_user = NULL; + p_stream->psz_password = NULL; + } + + p_stream->b_stream = VLC_TRUE; + p_stream->p_sys = NULL; + p_stream->pf_fill = NULL; + p_stream->i_buffer_size = 1024*1024; + p_stream->i_buffer_valid = 0; + p_stream->i_buffer = 0; + p_stream->p_buffer = malloc( p_stream->i_buffer_size ); + + __RegisterFile( p_httpt, p_stream ); + + vlc_mutex_unlock( &p_httpt->file_lock ); + + return p_stream; +} +static httpd_stream_t *RegisterStream( httpd_t *p_httpd, + char *psz_file, char *psz_mime, + char *psz_user, char *psz_password ) +{ + return( _RegisterStream( p_httpd->p_sys, + psz_file, psz_mime, psz_user, psz_password ) ); +} + +static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file ) +{ + int i; + + vlc_mutex_lock( &p_httpt->file_lock ); + for( i = 0; i < p_httpt->i_file_count; i++ ) + { + if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) ) + { + break; + } + } + if( i >= p_httpt->i_file_count ) + { + vlc_mutex_unlock( &p_httpt->file_lock ); + msg_Err( p_httpt, "cannot unregister file" ); + return; + } + + if( p_file->i_ref > 0 ) + { + httpd_connection_t *p_con; + /* force closing all connection for this file */ + msg_Err( p_httpt, "closing all client connection" ); + + vlc_mutex_lock( &p_httpt->connection_lock ); + for( p_con = p_httpt->p_first_connection; p_con != NULL; ) + { + httpd_connection_t *p_next; + + p_next = p_con->p_next; + if( p_con->p_file == p_file ) + { + httpd_ConnnectionClose( p_httpt, p_con ); + } + p_con = p_next; + } + vlc_mutex_unlock( &p_httpt->connection_lock ); + } + + FREE( p_file->psz_file ); + FREE( p_file->psz_mime ); + if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE ) + { + FREE( p_file->psz_user ); + FREE( p_file->psz_password ); + } + FREE( p_file->p_buffer ); + + FREE( p_file ); + + + if( p_httpt->i_file_count == 1 ) + { + FREE( p_httpt->file ); + p_httpt->i_file_count = 0; + } + else + { + int i_move; + + i_move = p_httpt->i_file_count - i - 1; + if( i_move > 0 ) + { + memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move ); + } + p_httpt->i_file_count--; + p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count ); + } + + vlc_mutex_unlock( &p_httpt->file_lock ); +} +static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file ) +{ + _UnregisterFile( p_httpd->p_sys, p_file ); +} + +static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream ) +{ + _UnregisterFile( p_httpd->p_sys, p_stream ); +} + + + +static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data ) +{ + if( i_data <= 0 ) + { + return( VLC_SUCCESS ); + } + + vlc_mutex_lock( &p_httpt->file_lock ); + if( p_stream->i_buffer_size < p_stream->i_buffer_valid + i_data ) + { + /* not enough room */ + if( p_stream->i_buffer < p_stream->i_buffer_valid ) + { + memmove( p_stream->p_buffer, + p_stream->p_buffer + p_stream->i_buffer, + p_stream->i_buffer_valid - p_stream->i_buffer ); + } + p_stream->i_buffer_valid -= p_stream->i_buffer; + + p_stream->i_buffer = 0; + } + i_data = __MIN( i_data, p_stream->i_buffer_size - p_stream->i_buffer_valid ); + memcpy( p_stream->p_buffer + p_stream->i_buffer_valid, p_data, i_data ); + p_stream->i_buffer_valid += i_data; + + vlc_mutex_unlock( &p_httpt->file_lock ); + + return( VLC_SUCCESS ); +} +static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data ) +{ + return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) ); +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static int httpd_page_401_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data ) +{ + char *p; + + p = *pp_data = malloc( 1024 ); + + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "Error 401\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "

401 authentification needed

\n" ); + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + + *pi_data = strlen( *pp_data ) + 1; + + return VLC_SUCCESS; +} +static int httpd_page_404_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data ) +{ + char *p; + + p = *pp_data = malloc( 1024 ); + + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "Error 404\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "

404 Ressource not found

\n" ); + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + + *pi_data = strlen( *pp_data ) + 1; + + return VLC_SUCCESS; +} + +static int httpd_page_admin_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data ) +{ + httpd_sys_t *p_httpt = (httpd_sys_t*)p_args; + httpd_connection_t *p_con; + char *p; + int i; + + /* FIXME FIXME do not use static size FIXME FIXME*/ + p = *pp_data = malloc( 8096 ); + + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "VideoLAN Client Stream Output\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "

VideoLAN Client Stream Output

\n" ); + p += sprintf( p, "

Admin page

\n" ); + + /* host list */ + vlc_mutex_lock( &p_httpt->host_lock ); + p += sprintf( p, "

Host list

\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n\n\n" ); + + for( i = 0; i < p_httpt->i_host_count; i++ ) + { + p += sprintf( p, "\n" ); + p += sprintf( p, "\n", p_httpt->host[i]->psz_host_addr ); + p += sprintf( p, "\n", p_httpt->host[i]->i_port ); + p += sprintf( p, "\n", inet_ntoa( p_httpt->host[i]->sock.sin_addr ) ); + p += sprintf( p, "\n" ); + } + p += sprintf( p, "
HostPortIP
%s%d%s
\n" ); + vlc_mutex_unlock( &p_httpt->host_lock ); + + /* file list */ + /* XXX do not take lock on file_lock */ + p += sprintf( p, "

File list

\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n\n\n" ); + + for( i = 0; i < p_httpt->i_file_count; i++ ) + { + if( !p_httpt->file[i]->b_stream ) + { + p += sprintf( p, "\n" ); + p += sprintf( p, "\n", p_httpt->file[i]->psz_file ); + p += sprintf( p, "\n", p_httpt->file[i]->psz_mime ); + p += sprintf( p, "\n", p_httpt->file[i]->psz_user ? "Yes" : "No" ); + p += sprintf( p, "\n", p_httpt->file[i]->i_ref); + p += sprintf( p, "\n" ); + } + } + p += sprintf( p, "
NameMimeProtectedUsed
%s%s%s%d
\n" ); + + /* stream list */ + /* XXX do not take lock on file_lock */ + p += sprintf( p, "

Stream list

\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n\n\n" ); + + for( i = 0; i < p_httpt->i_file_count; i++ ) + { + if( p_httpt->file[i]->b_stream ) + { + p += sprintf( p, "\n" ); + p += sprintf( p, "\n", p_httpt->file[i]->psz_file ); + p += sprintf( p, "\n", p_httpt->file[i]->psz_mime ); + p += sprintf( p, "\n", p_httpt->file[i]->psz_user ? "Yes" : "No" ); + p += sprintf( p, "\n", p_httpt->file[i]->i_ref); + p += sprintf( p, "\n" ); + } + } + p += sprintf( p, "
NameMimeProtectedUsed
%s%s%s%d
\n" ); + + /* connection list */ + /* XXX do not take lock on connection_lock */ + p += sprintf( p, "

Connection list

\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n\n\n" ); + + for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next ) + { + p += sprintf( p, "\n" ); + p += sprintf( p, "\n", inet_ntoa( p_con->sock.sin_addr ) ); + p += sprintf( p, "\n", p_con->psz_file ); + p += sprintf( p, "\n", p_con->i_http_error ); + p += sprintf( p, "\n" ); + } + p += sprintf( p, "
IPRequested FileStatus
%s%s%d
\n" ); + + + /* www.videolan.org */ + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + + *pi_data = strlen( *pp_data ) + 1; + + return VLC_SUCCESS; +} + + +static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock ) +{ + httpd_connection_t *p_con; + + msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) ); + + /* create a new connection and link it */ + p_con = malloc( sizeof( httpd_connection_t ) ); + p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST; + p_con->fd = fd; + p_con->sock = *p_sock; + p_con->psz_file = NULL; + p_con->i_http_error = 0; + p_con->psz_user = NULL; + p_con->psz_password = NULL; + p_con->p_file = NULL; + + p_con->i_buffer = 0; + p_con->i_buffer_size = 8096; + p_con->p_buffer = malloc( p_con->i_buffer_size ); + + p_con->p_next = NULL; + + if( p_httpt->p_first_connection ) + { + httpd_connection_t *p_last; + + p_last = p_httpt->p_first_connection; + while( p_last->p_next ) + { + p_last = p_last->p_next; + } + + p_last->p_next = p_con; + p_con->p_prev = p_last; + } + else + { + p_con->p_prev = NULL; + + p_httpt->p_first_connection = p_con; + } + + p_httpt->i_connection_count++; +} + +static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con ) +{ + msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) ); + + p_httpt->i_connection_count--; + /* first cut out from list */ + if( p_con->p_prev ) + { + p_con->p_prev->p_next = p_con->p_next; + } + else + { + p_httpt->p_first_connection = p_con->p_next; + } + + if( p_con->p_next ) + { + p_con->p_next->p_prev = p_con->p_prev; + } + + if( p_con->p_file ) p_con->p_file->i_ref--; + FREE( p_con->psz_file ); + + FREE( p_con->p_buffer ); + SOCKET_CLOSE( p_con->fd ); + + + FREE( p_con->psz_user ); + FREE( p_con->psz_password ); + + free( p_con ); +} + +static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end ) +{ + char *p = *pp_buffer; + int i; + + while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) ) + { + p++; + } + + i = 0; + for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++) + { + word[i] = *p; + } + + word[__MIN( i, i_word_max -1 )] = '\0'; + + *pp_buffer = p; + +} +static int httpd_RequestNextLine( char **pp_buffer, char *p_end ) +{ + char *p; + + for( p = *pp_buffer; p < p_end; p++ ) + { + if( p + 1 < p_end && *p == '\n' ) + { + *pp_buffer = p + 1; + return VLC_SUCCESS; + } + if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' ) + { + *pp_buffer = p + 2; + return VLC_SUCCESS; + } + } + *pp_buffer = p_end; + return VLC_EGENERIC; +} + +//char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static void b64_decode( char *dest, char *src ) +{ + int i_level; + int last = 0; + int b64[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + + for( i_level = 0; *src != '\0' > 0; src++ ) + { + int c; + + c = b64[(unsigned int)*src]; + if( c == -1 ) + { + src++; + continue; + } + + switch( i_level ) + { + case 0: + i_level++; + break; + case 1: + *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 ); + i_level++; + break; + case 2: + *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f ); + i_level++; + break; + case 3: + *dest++ = ( ( last &0x03 ) << 6 ) | c; + i_level = 0; + } + last = c; + } + + *dest = '\0'; +} + +static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con ) +{ + char *psz_status; + char *p, *p_end; + + int i; + char command[32]; + char url[1024]; + char version[32]; + char user[512] = ""; + char password[512] = ""; + + //msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer ); + + p = p_con->p_buffer; + p_end = p + strlen( p ) + 1; + + httpd_RequestGetWord( command, 32, &p, p_end ); + httpd_RequestGetWord( url, 1024, &p, p_end ); + httpd_RequestGetWord( version, 32, &p, p_end ); + //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version ); + + if( strcmp( command, "GET" ) ) + { + /* unimplemented */ + p_con->psz_file = strdup( "/501.html" ); + p_con->i_http_error = 501; + goto create_header; + } + + if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) ) + { + p_con->psz_file = strdup( "/505.html" ); + p_con->i_http_error = 505; + + goto create_header; + } + + /* parse headers */ + for( ;; ) + { + char header[1024]; + + if( httpd_RequestNextLine( &p, p_end ) ) + { + //msg_Dbg( p_httpt, "failled new line" ); + break;; + } + //msg_Dbg( p_httpt, "new line=%s", p ); + + httpd_RequestGetWord( header, 1024, &p, p_end ); + + if( !strcmp( header, "Authorization:" ) ) + { + char method[32]; + + httpd_RequestGetWord( method, 32, &p, p_end ); + if( !strcasecmp( method, "BASIC" ) ) + { + char basic[1024]; + char decoded[1024]; + + httpd_RequestGetWord( basic, 1024, &p, p_end ); + //msg_Dbg( p_httpt, "Authorization: basic:%s", basic ); + b64_decode( decoded, basic ); + + //msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded ); + if( strchr( decoded, ':' ) ) + { + char *p = strchr( decoded, ':' ); + + p[0] = '\0'; p++; + strcpy( user, decoded ); + strcpy( password, p ); + } + } + } + } + + p_con->psz_file = strdup( url ); + p_con->i_http_error = 200; + +create_header: + //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error ); + FREE( p_con->p_buffer ); + p_con->i_buffer = 0; + p_con->i_buffer_size = 0; + + //vlc_mutex_lock( &p_httpt->file_lock ); +search_file: + /* search file */ + for( i = 0, p_con->p_file = NULL; i < p_httpt->i_file_count; i++ ) + { + if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) ) + { + p_con->p_file = p_httpt->file[i]; + } + } + + if( !p_con->p_file ) + { + p_con->psz_file = strdup( "/404.html" ); + p_con->i_http_error = 404; + + /* XXX be sure that "/404.html" exist else ... */ + goto search_file; + } + + if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC ) + { + if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) ) + { + p_con->psz_file = strdup( "/401.html" ); + strcpy( user, p_con->p_file->psz_user ); + p_con->i_http_error = 401; + + /* XXX do not put password on 404 else ... */ + goto search_file; + } + } + + p_con->p_file->i_ref++; +// vlc_mutex_unlock( &p_httpt->file_lock ); + + switch( p_con->i_http_error ) + { + case 200: + psz_status = "OK"; + break; + + case 401: + psz_status = "Authorization Required"; + break; + default: + psz_status = "Unknown"; + break; + } + + p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER; + + p_con->i_buffer_size = 4096; + p_con->i_buffer = 0; + p = p_con->p_buffer = malloc( p_con->i_buffer_size ); + + p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status ); + p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime ); + if( p_con->i_http_error == 401 ) + { + p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user ); + } + p += sprintf( p, "\r\n" ); + + p_con->i_buffer_size = strlen( p_con->p_buffer ) + 1; + + //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer ); +} +#define HTTPD_STREAM_PACKET 1300 +static void httpd_Thread( httpd_sys_t *p_httpt ) +{ + httpd_file_t *p_page_admin; + httpd_file_t *p_page_401; + httpd_file_t *p_page_404; + + httpd_connection_t *p_con; + vlc_bool_t b_wait; + + msg_Info( p_httpt, "httpd started" ); + + p_page_401 = _RegisterFile( p_httpt, + "/401.html", "text/html", + NULL, NULL, + httpd_page_401_fill, + (httpd_file_callback_args_t*)NULL ); + p_page_404 = _RegisterFile( p_httpt, + "/404.html", "text/html", + NULL, NULL, + httpd_page_404_fill, + (httpd_file_callback_args_t*)NULL ); + p_page_admin = _RegisterFile( p_httpt, + "/admin.html", "text/html", + "admin", "salut", + httpd_page_admin_fill, + (httpd_file_callback_args_t*)p_httpt ); + + while( !p_httpt->b_die ) + { + int i; + if( p_httpt->i_host_count <= 0 ) + { + msleep( 100 * 1000 ); + continue; + } + vlc_mutex_lock( &p_httpt->host_lock ); + /* accept/refuse new connection */ + for( i = 0; i < p_httpt->i_host_count; i++ ) + { + int i_sock_size = sizeof( struct sockaddr_in ); + struct sockaddr_in sock; + int fd; + + fd = accept( p_httpt->host[i]->fd, &sock, &i_sock_size ); + if( fd > 0 ) + { + fcntl( fd, F_SETFL, O_NONBLOCK ); + + if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION ) + { + msg_Warn( p_httpt, "max connection reached" ); + SOCKET_CLOSE( fd ); + continue; + } + /* create a new connection and link it */ + httpd_ConnnectionNew( p_httpt, fd, &sock ); + + } + } + vlc_mutex_unlock( &p_httpt->host_lock ); + + vlc_mutex_lock( &p_httpt->file_lock ); + /* now do work for all connections */ + for( p_con = p_httpt->p_first_connection; p_con != NULL; ) + { + if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST ) + { + int i_len; + /* read data */ + i_len = recv( p_con->fd, + p_con->p_buffer + p_con->i_buffer, + p_con->i_buffer_size - p_con->i_buffer, 0 ); + + if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )|| + ( i_len == 0 ) ) + { + httpd_connection_t *p_next = p_con->p_next; + + httpd_ConnnectionClose( p_httpt, p_con ); + p_con = p_next; + } + else if( i_len > 0 ) + { + uint8_t *ptr; + + p_con->i_buffer += i_len; + + ptr = p_con->p_buffer + p_con->i_buffer; + + if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )|| + ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) || + p_con->i_buffer >= p_con->i_buffer_size ) + { + p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0'; + httpd_ConnectionParseRequest( p_httpt, p_con ); + } + + p_con = p_con->p_next; + } + else + { + p_con = p_con->p_next; + } + continue; /* just for clarity */ + } + else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE ) + { + int i_len; + + /* write data */ + i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 ); + +// 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 ); + + if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )|| + ( i_len == 0 ) ) + { + httpd_connection_t *p_next = p_con->p_next; + + httpd_ConnnectionClose( p_httpt, p_con ); + p_con = p_next; + } + else if( i_len > 0 ) + { + p_con->i_buffer += i_len; + + if( p_con->i_buffer >= p_con->i_buffer_size ) + { + if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER ) + { + p_con->i_buffer_size = 0; + p_con->i_buffer = 0; + FREE( p_con->p_buffer ); + + if( !p_con->p_file->b_stream ) + { + p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER + p_con->p_file->pf_fill( p_con->p_file->p_sys, &p_con->p_buffer, &p_con->i_buffer_size ); + } + else + { + p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM; + } + p_con = p_con->p_next; + } + else + { + httpd_connection_t *p_next = p_con->p_next; + + httpd_ConnnectionClose( p_httpt, p_con ); + p_con = p_next; + } + } + else + { + p_con = p_con->p_next; + } + } + else + { + p_con = p_con->p_next; + } + continue; /* just for clarity */ + } + else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM ) + { + httpd_file_t *p_file = p_con->p_file; + int i_len; + + //msg_Dbg( p_httpt, "buffer=%d buffer_size=%d", p_file->i_buffer, p_file->i_buffer_size ); + if( p_file->i_buffer < p_file->i_buffer_valid ) + { + int i_write; + /* write data */ + i_write = __MIN( p_file->i_buffer_valid - p_file->i_buffer, HTTPD_STREAM_PACKET ); + i_len = send( p_con->fd, p_file->p_buffer + p_file->i_buffer, i_write, 0 ); + + if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )|| + ( i_len == 0 ) ) + { + httpd_connection_t *p_next = p_con->p_next; + + httpd_ConnnectionClose( p_httpt, p_con ); + p_con = p_next; + } + else + { + p_con = p_con->p_next; + } + } + else + { + p_con = p_con->p_next; + } + continue; /* just for clarity */ + } + else + { + msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" ); + p_con = p_con->p_next; + } + } /* for over connection */ + + + b_wait = VLC_TRUE; + /* update position for stream based file */ + for( i = 0; i < p_httpt->i_file_count; i++ ) + { + if( p_httpt->file[i]->b_stream ) + { + p_httpt->file[i]->i_buffer += __MIN( p_httpt->file[i]->i_buffer_valid - p_httpt->file[i]->i_buffer, + HTTPD_STREAM_PACKET ); + if( p_httpt->file[i]->i_buffer < p_httpt->file[i]->i_buffer_valid ) + { + /* there is data */ + b_wait = VLC_FALSE; + } + } + } + vlc_mutex_unlock( &p_httpt->file_lock ); + if( b_wait ) msleep( 100 ); + } + msg_Info( p_httpt, "httpd stopped" ); + + _UnregisterFile( p_httpt, p_page_401 ); + _UnregisterFile( p_httpt, p_page_404 ); + _UnregisterFile( p_httpt, p_page_admin ); +} + -- 2.39.5