From 2d4c56cd959c58efb6200487bd0e1edf33fa5e9d Mon Sep 17 00:00:00 2001 From: Laurent Aimar Date: Wed, 3 Mar 2004 13:23:47 +0000 Subject: [PATCH] * include/httpd.h, modules/misc/httpd.c: remove old http daemon. * include/vlc_httpd.h src/misc/httpd.c: added new http daemon, it will allow http 1.1, redirection, RTSP, ... --- include/httpd.h | 152 --- include/vlc_httpd.h | 154 +++ modules/misc/httpd.c | 2251 ------------------------------------------ src/misc/httpd.c | 2125 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 2279 insertions(+), 2403 deletions(-) delete mode 100644 include/httpd.h create mode 100644 include/vlc_httpd.h delete mode 100644 modules/misc/httpd.c create mode 100644 src/misc/httpd.c diff --git a/include/httpd.h b/include/httpd.h deleted file mode 100644 index 6d04a8dc7a..0000000000 --- a/include/httpd.h +++ /dev/null @@ -1,152 +0,0 @@ -/***************************************************************************** - * httpd.h - ***************************************************************************** - * Copyright (C) 2001-2003 VideoLAN - * $Id: httpd.h,v 1.7 2003/07/11 09:50:10 gbazin 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_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 *p_request, int i_request, uint8_t **pp_data, int *pi_data ); - -typedef struct httpd_sys_t httpd_sys_t; - -enum httpdControl_e -{ - HTTPD_GET_HOSTS, - HTTPD_GET_URLS, - HTTPD_GET_CONNECTIONS, - HTTPD_GET_ACL, /* not implemented */ - - HTTPD_SET_CLOSE, - HTTPD_SET_ACL /* not implemented */ -}; - -typedef struct -{ - char *psz_name; - char *psz_value; -} httpd_val_t; - -typedef struct -{ - int i_count; - httpd_val_t *info; -} httpd_info_t; - - -typedef struct httpd_t httpd_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_get, - httpd_file_callback pf_post, - 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 ); - int (*pf_header_stream) ( httpd_t *, - httpd_stream_t *, - uint8_t *, int ); - void (*pf_unregister_stream) ( httpd_t *, httpd_stream_t * ); - int (*pf_control) ( httpd_t *, - int i_query, - void *arg1, void *arg2 ); -}; - - -/***************************************************************************** - * httpd_Find: - * Return the running httpd instance (if none and b_create then a new one is created) - * httpd_release: - *****************************************************************************/ - -static inline httpd_t* httpd_Find( vlc_object_t *p_this, vlc_bool_t b_create ) -{ - httpd_t *p_httpd = NULL; - vlc_value_t lockval; - - var_Get( p_this->p_libvlc, "httpd", &lockval ); - vlc_mutex_lock( lockval.p_address ); - - p_httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE ); - if( !p_httpd && b_create) - { - msg_Info(p_this, "creating new http daemon" ); - - p_httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ); - if( !p_httpd ) - { - msg_Err( p_this, "out of memory" ); - vlc_mutex_unlock( lockval.p_address ); - 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 ); - vlc_mutex_unlock( lockval.p_address ); - return( NULL ); - } - - vlc_object_yield( p_httpd ); - vlc_object_attach( p_httpd, p_this->p_vlc ); - } - vlc_mutex_unlock( lockval.p_address ); - - return( p_httpd ); -} - -static inline void httpd_Release( httpd_t *p_httpd ) -{ - vlc_object_release( p_httpd ); - - if( p_httpd->i_refcount <= 0 ) - { - msg_Info( p_httpd, "destroying unused httpd" ); - vlc_object_detach( p_httpd ); - module_Unneed( p_httpd, p_httpd->p_module ); - vlc_object_destroy( p_httpd ); - } -} diff --git a/include/vlc_httpd.h b/include/vlc_httpd.h new file mode 100644 index 0000000000..cd95b7f6fb --- /dev/null +++ b/include/vlc_httpd.h @@ -0,0 +1,154 @@ +/***************************************************************************** + * vlc_httpd.h: builtin HTTP/RTSP server. + ***************************************************************************** + * Copyright (C) 2004 VideoLAN + * $Id: vlc_httpd.h,v 1.1 2004/03/03 13:23:47 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. + *****************************************************************************/ + +#ifndef _VLC_HTTPD_H +#define _VLC_HTTPD_H 1 + +/* NEVER touch that, it's here only because src/misc/objects.c + * need sizeof(httpd_t) */ +struct httpd_t +{ + VLC_COMMON_MEMBERS + + int i_host; + httpd_host_t **host; +}; + +enum +{ + HTTPD_MSG_NONE, + + /* answer */ + HTTPD_MSG_ANSWER, + + /* channel communication */ + HTTPD_MSG_CHANNEL, + + /* http request */ + HTTPD_MSG_GET, + HTTPD_MSG_HEAD, + HTTPD_MSG_POST, + + /* rtsp request */ + HTTPD_MSG_OPTIONS, + HTTPD_MSG_DESCRIBE, + HTTPD_MSG_SETUP, + HTTPD_MSG_PLAY, + HTTPD_MSG_PAUSE, + HTTPD_MSG_TEARDOWN, + + /* just to track the count of MSG */ + HTTPD_MSG_MAX +}; + +enum +{ + HTTPD_PROTO_NONE, + HTTPD_PROTO_HTTP, + HTTPD_PROTO_RTSP, +}; + +struct httpd_message_t +{ + httpd_client_t *cl; /* NULL if not throught a connection e vlc internal */ + + int i_type; + int i_proto; + int i_version; + + /* for an answer */ + int i_status; + char *psz_status; + + /* for a query */ + char *psz_url; + char *psz_args; /* FIXME find a clean way to handle GET(psz_args) and POST(body) through the same code */ + + /* for rtp over rtsp */ + int i_channel; + + /* options */ + int i_name; + char **name; + int i_value; + char **value; + + /* body */ + int64_t i_body_offset; + int i_body; + uint8_t *p_body; + +}; + +/* I keep the definition here, easier than looking at vlc_common.h + + * answer could be null, int this case no answer is requested +typedef int (*httpd_callback_t)( httpd_callback_sys_t *, httpd_client_t *, httpd_message_t *answer, httpd_message_t *query ); +typedef struct httpd_callback_sys_t httpd_callback_sys_t; + +typedef struct httpd_file_t httpd_file_t; +typedef struct httpd_file_sys_t httpd_file_sys_t; +typedef int (*httpd_file_callback_t)( httpd_file_sys_t*, httpd_file_t *, uint8_t *psz_request, uint8_t **pp_data, int *pi_data ); +*/ + +/* create a new host */ +VLC_EXPORT( httpd_host_t *, httpd_HostNew, ( vlc_object_t *, char *psz_host, int i_port ) ); +/* delete a host */ +VLC_EXPORT( void, httpd_HostDelete, ( httpd_host_t * ) ); + +/* register a new url */ +VLC_EXPORT( httpd_url_t *, httpd_UrlNew, ( httpd_host_t *, char *psz_url, char *psz_user, char *psz_password ) ); +VLC_EXPORT( httpd_url_t *, httpd_UrlNewUnique, ( httpd_host_t *, char *psz_url, char *psz_user, char *psz_password ) ); +/* register callback on a url */ +VLC_EXPORT( int, httpd_UrlCatch, ( httpd_url_t *, int i_msg, httpd_callback_t, httpd_callback_sys_t * ) ); +/* delete an url */ +VLC_EXPORT( void, httpd_UrlDelete, ( httpd_url_t * ) ); + +/* Default client mode is FILE, use these to change it */ +VLC_EXPORT( void, httpd_ClientModeStream, ( httpd_client_t *cl ) ); +VLC_EXPORT( void, httpd_ClientModeBidir, ( httpd_client_t *cl ) ); + +/* High level */ + +VLC_EXPORT( httpd_file_t *, httpd_FileNew, ( httpd_host_t *, char *psz_url, char *psz_mime, char *psz_user, char *psz_password, httpd_file_callback_t pf_fill, httpd_file_sys_t * ) ); +VLC_EXPORT( void, httpd_FileDelete, ( httpd_file_t * ) ); + + +VLC_EXPORT( httpd_redirect_t *, httpd_RedirectNew, ( httpd_host_t *, char *psz_url_dst, char *psz_url_src ) ); +VLC_EXPORT( void, httpd_RedirectDelete, ( httpd_redirect_t * ) ); + + +VLC_EXPORT( httpd_stream_t *, httpd_StreamNew, ( httpd_host_t *, char *psz_url, char *psz_mime, char *psz_user, char *psz_password ) ); +VLC_EXPORT( void, httpd_StreamDelete, ( httpd_stream_t * ) ); +VLC_EXPORT( int, httpd_StreamHeader, ( httpd_stream_t *, uint8_t *p_data, int i_data ) ); +VLC_EXPORT( int, httpd_StreamSend, ( httpd_stream_t *, uint8_t *p_data, int i_data ) ); + + +/* Msg functions facilities */ +VLC_EXPORT( void, httpd_MsgInit, ( httpd_message_t * ) ); +VLC_EXPORT( void, httpd_MsgAdd, ( httpd_message_t *, char *psz_name, char *psz_value, ... ) ); +/* return "" if not found. The string is not allocated */ +VLC_EXPORT( char *, httpd_MsgGet, ( httpd_message_t *, char *psz_name ) ); +VLC_EXPORT( void, httpd_MsgClean, ( httpd_message_t * ) ); + +#endif /* _VLC_HTTPD_H */ diff --git a/modules/misc/httpd.c b/modules/misc/httpd.c deleted file mode 100644 index 90eef94724..0000000000 --- a/modules/misc/httpd.c +++ /dev/null @@ -1,2251 +0,0 @@ -/***************************************************************************** - * httpd.c - ***************************************************************************** - * Copyright (C) 2001-2003 VideoLAN - * $Id: httpd.c,v 1.31 2004/02/05 19:51:46 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 - * - * TODO: - * - make that two distinct host:port use different daemon - *****************************************************************************/ -#include -#include - -#include - -#include -#include - -#include "httpd.h" - -#ifdef HAVE_SYS_TIME_H -# include -#endif - -#ifdef HAVE_UNISTD_H -# 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 512 -#define HTTPD_CONNECTION_MAX_UNUSED 10000000 - - -#define FREE( p ) if( p ) { free( p); (p) = NULL; } - -#if defined( WIN32 ) || defined( UNDER_CE ) -#define SOCKET_CLOSE(a) closesocket(a) -#else -#define SOCKET_CLOSE(a) close(a) -#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 ); - var_Create( p_module->p_libvlc, "httpd", VLC_VAR_MUTEX ); -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_get, - httpd_file_callback pf_post, - httpd_file_callback_args_t *p_args ); -static void UnregisterFile ( httpd_t *, httpd_file_t * ); - -#if 0 - #define httpd_stream_t httpd_file_t -#endif -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 int HeaderStream ( httpd_t *, httpd_stream_t *, uint8_t *, int ); -static void UnregisterStream( httpd_t *, httpd_stream_t* ); -static int Control ( httpd_t *, int , void*, void* ); -/***************************************************************************** - * Internal definitions - *****************************************************************************/ -struct httpd_host_t -{ - int i_ref; - - char *psz_host_addr; - int i_port; - - struct sockaddr_in sock; - int fd; - -}; - -enum httpd_authenticate_e -{ - HTTPD_AUTHENTICATE_NONE = 0, - HTTPD_AUTHENTICATE_BASIC = 1 -}; - -#if 0 - typedef httpd_file_t httpd_stream_t; -#endif - -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_get; /* it should allocate and fill *pp_data and *pi_data */ - httpd_file_callback pf_post; /* it should allocate and fill *pp_data and *pi_data */ - - /* private */ - - /* circular buffer for stream only */ - int i_buffer_size; /* buffer size, can't be reallocated smaller */ - uint8_t *p_buffer; /* buffer */ - int64_t i_buffer_pos; /* absolute position from begining */ - int64_t i_buffer_last_pos; /* a new connection will start with that */ - - /* data to be send at connection time (if any) */ - int i_header_size; - uint8_t *p_header; -}; - - -enum httpd_connection_state_e -{ - HTTPD_CONNECTION_RECEIVING_REQUEST = 1, - HTTPD_CONNECTION_SENDING_HEADER = 2, - HTTPD_CONNECTION_SENDING_FILE = 3, - HTTPD_CONNECTION_SENDING_STREAM = 4, - HTTPD_CONNECTION_TO_BE_CLOSED = 5 -}; - -enum httpd_connection_method_e -{ - HTTPD_CONNECTION_METHOD_GET = 1, - HTTPD_CONNECTION_METHOD_POST = 2, - HTTPD_CONNECTION_METHOD_HEAD = 3, - /* cludgy, only used when parsing connection request */ - HTTPD_CONNECTION_METHOD_ASFHEAD = 4 -}; - -typedef struct httpd_connection_s -{ - struct httpd_connection_s *p_next; - struct httpd_connection_s *p_prev; - - struct sockaddr_in sock; - int fd; - mtime_t i_last_activity_date; - - int i_state; - int i_method; /* get/post */ - - 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; - - uint8_t *p_request; /* whith get: ?<*>, with post: main data */ - int i_request_size; - - httpd_file_t *p_file; - - /* used while sending header and file */ - int i_buffer_size; - uint8_t *p_buffer; - int i_buffer; /* private */ - - /* used for stream */ - int64_t i_stream_pos; /* absolute pos in stream */ -} httpd_connection_t; - -/* Linked List of banned IP */ -typedef struct httpd_banned_ip_s -{ - struct httpd_banned_ip_s *p_next; - struct httpd_banned_ip_s *p_prev; - - char *psz_ip; - -} httpd_banned_ip_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; - - vlc_mutex_t ban_lock; - int i_banned_ip_count; - httpd_banned_ip_t *p_first_banned_ip; -}; - -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 * ); -static int httpd_UnbanIP( httpd_sys_t *, httpd_banned_ip_t *); -#if 0 -static int httpd_BanIP( httpd_sys_t *, char *); -#endif -static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *, char * ); - -/***************************************************************************** - * 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 */ - if( vlc_mutex_init( p_httpd, &p_httpt->host_lock ) ) - { - msg_Err( p_httpd, "Error in mutex creation"); - return( VLC_EGENERIC ); - } - p_httpt->i_host_count = 0; - p_httpt->host = NULL; - - if( vlc_mutex_init( p_httpd, &p_httpt->file_lock ) ) - { - msg_Err( p_httpd, "Error in mutex creation"); - return( VLC_EGENERIC ); - } - p_httpt->i_file_count = 0; - p_httpt->file = NULL; - - if( vlc_mutex_init( p_httpd, &p_httpt->connection_lock ) ) - { - msg_Err( p_httpd, "Error in mutex creation"); - return( VLC_EGENERIC ); - } - p_httpt->i_connection_count = 0; - p_httpt->p_first_connection = NULL; - - if( vlc_mutex_init( p_httpd, &p_httpt->ban_lock ) ) - { - msg_Err( p_httpd, "Error in mutex creation"); - return( VLC_EGENERIC ); - } - - p_httpt->i_banned_ip_count = 0; - p_httpt->p_first_banned_ip = 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_mutex_destroy( &p_httpt->ban_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_header_stream = HeaderStream; - p_httpd->pf_send_stream = SendStream; - p_httpd->pf_unregister_stream=UnregisterStream; - p_httpd->pf_control = Control; - - 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; - httpd_banned_ip_t *p_banned_ip; - - 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 ); - } - - /* Free all banned IP */ - vlc_mutex_destroy( &p_httpt->ban_lock ); - while( ( p_banned_ip = p_httpt->p_first_banned_ip)) - { - httpd_UnbanIP(p_httpt,p_banned_ip); - } - - 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; -#if !defined( WIN32 ) && !defined( UNDER_CE ) - int i_flags; -#endif - - 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 == INADDR_ANY || - 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( p_httpt->host[i] ); - } - - /* 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, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 ) - { - msg_Err( p_httpt, "cannot bind socket" ); - goto socket_failed; - } - /* set to non-blocking */ -#if defined( WIN32 ) || defined( UNDER_CE ) - { - unsigned long i_dummy = 1; - if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 ) - { - msg_Err( p_httpt, "cannot set socket to non-blocking mode" ); - goto socket_failed; - } - } -#else - 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; - } -#endif - /* 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 *) ); - } - if( !p_httpt->host ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - p_host = malloc( sizeof( httpd_host_t ) ); - if( !p_host ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - p_host->i_ref = 1; - p_host->psz_host_addr = strdup( psz_host_addr ); - if( !p_host->psz_host_addr ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - 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 * ) ); - if( !p_httpt->host ) - { - msg_Err( p_httpt, "Out of memory" ); - return; - } - } - - 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 *) ); - } - if( !p_httpt->file ) - { - return; - } - 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_get, - httpd_file_callback pf_post, - 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 ) ); - if( !p_file ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - p_file->i_ref = 0; - p_file->psz_file = strdup( psz_file ); - p_file->psz_mime = strdup( psz_mime ); - if( !p_file->psz_file || !p_file->psz_mime ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - 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 ); - if( !p_file->psz_user || !p_file->psz_password ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - } - 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_get = pf_get; - p_file->pf_post = pf_post; - - p_file->i_buffer_size = 0; - p_file->i_buffer_last_pos = 0; - p_file->i_buffer_pos = 0; - p_file->p_buffer = NULL; - - p_file->i_header_size = 0; - p_file->p_header = 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_get, - httpd_file_callback pf_post, - httpd_file_callback_args_t *p_args ) -{ - return( _RegisterFile( p_httpd->p_sys, - psz_file, psz_mime, psz_user, psz_password, - pf_get, pf_post, 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 registered", psz_file ); - return NULL; - } - - p_stream = malloc( sizeof( httpd_stream_t ) ); - if( !p_stream ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - p_stream->i_ref = 0; - p_stream->psz_file = strdup( psz_file ); - p_stream->psz_mime = strdup( psz_mime ); - if( !p_stream->psz_file || !p_stream->psz_mime ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - 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 ); - if( !p_stream->psz_user || !p_stream->psz_password ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - } - 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_get = NULL; - p_stream->pf_post = NULL; - - p_stream->i_buffer_size = 5*1024*1024; - p_stream->i_buffer_pos = 0; - p_stream->i_buffer_last_pos = 0; - p_stream->p_buffer = malloc( p_stream->i_buffer_size ); - if( !p_stream->p_buffer ) - { - msg_Err( p_httpt, "Out of memory" ); - return NULL; - } - - p_stream->i_header_size = 0; - p_stream->p_header = NULL; - - __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->p_header ); - - 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 ) -{ - int i_count; - int i_pos; - - if( i_data <= 0 || p_data == NULL ) - { - return( VLC_SUCCESS ); - } -#if 0 - fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos ); -#endif - vlc_mutex_lock( &p_httpt->file_lock ); - - /* save this pointer (to be used by new connection) */ - p_stream->i_buffer_last_pos = p_stream->i_buffer_pos; - - i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size; - i_count = i_data; - while( i_count > 0) - { - int i_copy; - - i_copy = __MIN( i_count, p_stream->i_buffer_size - i_pos ); - - /* Ok, we can't go past the end of our buffer */ - memcpy( &p_stream->p_buffer[i_pos], - p_data, - i_copy ); - - i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size; - i_count -= i_copy; - p_data += i_copy; - } - - p_stream->i_buffer_pos += 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 HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data ) -{ - httpd_sys_t *p_httpt = p_httpd->p_sys; - - vlc_mutex_lock( &p_httpt->file_lock ); - - FREE( p_stream->p_header ); - if( p_data == NULL || i_data <= 0 ) - { - p_stream->i_header_size = 0; - } - else - { - p_stream->i_header_size = i_data; - p_stream->p_header = malloc( i_data ); - if( !p_stream->p_header ) - { - msg_Err( p_httpt, "Out of memory" ); - return( VLC_ENOMEM ); - } - memcpy( p_stream->p_header, - p_data, - i_data ); - } - vlc_mutex_unlock( &p_httpt->file_lock ); - - return( VLC_SUCCESS ); -} - -static void httpd_info_add_ss( httpd_info_t *p_info, char *name, char *value ) -{ - if( p_info->i_count == 0 ) - { - p_info->info = malloc( sizeof( httpd_val_t ) ); - } - else - { - p_info->info = - realloc( p_info->info, - sizeof( httpd_val_t ) * ( p_info->i_count + 1 ) ); - } - if( !p_info->info ) - { - return; - } - p_info->info[p_info->i_count].psz_name = strdup( name ); - if( ! p_info->info[p_info->i_count].psz_name ) - { - return; - } - p_info->info[p_info->i_count].psz_value = strdup( value ? value : "(null)"); - p_info->i_count++; -} - -static void httpd_info_add_si( httpd_info_t *p_info, char *name, int i_value ) -{ - char v[40]; /* Ok, int is not so long */ - - sprintf( v, "%d", i_value ); - httpd_info_add_ss( p_info, name, v ); -} - -static void httpd_info_add_sp( httpd_info_t *p_info, char *name, void *value ) -{ - char v[40]; - - sprintf( v, "%p", value ); - httpd_info_add_ss( p_info, name, v ); -} - - -static int Control( httpd_t *p_httpd, - int i_query, void *arg1, void *arg2 ) -{ - httpd_sys_t *p_httpt = p_httpd->p_sys; - httpd_info_t *p_info; - httpd_connection_t *p_con; - int i; - void *id; - - switch( i_query ) - { - case HTTPD_GET_HOSTS: - p_info = arg1; - p_info->i_count = 0; - vlc_mutex_lock( &p_httpt->host_lock ); - for( i = 0; i < p_httpt->i_host_count; i++ ) - { - httpd_info_add_sp( p_info, - "id", p_httpt->host[i] ); - httpd_info_add_ss( p_info, - "host", p_httpt->host[i]->psz_host_addr ); - httpd_info_add_ss( p_info, - "ip", - inet_ntoa(p_httpt->host[i]->sock.sin_addr)); - httpd_info_add_si( p_info, - "port", p_httpt->host[i]->i_port ); - } - vlc_mutex_unlock( &p_httpt->host_lock ); - return VLC_SUCCESS; - case HTTPD_GET_URLS: - p_info = arg1; - p_info->i_count = 0; - /* we can't take file_lock */ - for( i = 0; i < p_httpt->i_file_count; i++ ) - { - httpd_info_add_sp( p_info, - "id", p_httpt->file[i] ); - httpd_info_add_si( p_info, - "stream", p_httpt->file[i]->b_stream ? 1 : 0 ); - httpd_info_add_ss( p_info, - "url", p_httpt->file[i]->psz_file ); - httpd_info_add_ss( p_info, - "mime", p_httpt->file[i]->psz_mime ); - httpd_info_add_si( p_info, - "protected", p_httpt->file[i]->psz_user ? 1 : 0 ); - httpd_info_add_si( p_info, - "used", p_httpt->file[i]->i_ref ); - } - return VLC_SUCCESS; - case HTTPD_GET_CONNECTIONS: - p_info = arg1; - p_info->i_count = 0; - /* we can't take lock */ - for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next ) - { - if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED ) - { - httpd_info_add_sp( p_info, - "id", p_con ); - httpd_info_add_ss( p_info, - "ip", inet_ntoa( p_con->sock.sin_addr ) ); - httpd_info_add_ss( p_info, - "url", p_con->psz_file ); - httpd_info_add_si( p_info, - "status", p_con->i_http_error ); - } - } - return VLC_SUCCESS; - case HTTPD_GET_ACL: - p_info = arg1; - p_info->i_count = 0; - return VLC_EGENERIC; - - case HTTPD_SET_CLOSE: - sscanf( arg1, "%p", &id ); - fprintf( stderr, "Control: HTTPD_SET_CLOSE: id=%p", id ); - - for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next ) - { - if( (void*)p_con == id ) - { - /* XXX don't free p_con as it could be the one that it is sending ... */ - p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED; - return VLC_SUCCESS; - } - } - return VLC_EGENERIC; - - case HTTPD_SET_ACL: - sscanf( arg1, "%p", &id ); - fprintf( stderr, "Control: %p", id ); - default: - return VLC_EGENERIC; - } -} - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static int httpd_page_400_get( httpd_file_callback_args_t *p_args, - uint8_t *p_request, int i_request, - uint8_t **pp_data, int *pi_data ) -{ - char *p; - - p = *pp_data = malloc( 1024 ); - if( !p ) - { - return VLC_ENOMEM ; - } - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "Error 400\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "\n" ); - p += sprintf( p, "

400 Bad Request

\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_401_get( httpd_file_callback_args_t *p_args, - uint8_t *p_request, int i_request, - uint8_t **pp_data, int *pi_data ) -{ - char *p; - - p = *pp_data = malloc( 1024 ); - if( !p ) - { - return VLC_ENOMEM ; - } - - 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_get( httpd_file_callback_args_t *p_args, - uint8_t *p_request, int i_request, - uint8_t **pp_data, int *pi_data ) -{ - char *p; - - p = *pp_data = malloc( 1024 ); - if( !p ) - { - return VLC_ENOMEM ; - } - - 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; -} - -#if 0 -static int httpd_BanIP( httpd_sys_t *p_httpt, char * psz_new_banned_ip) -{ - httpd_banned_ip_t *p_new_banned_ip ; - - p_new_banned_ip = malloc( sizeof( httpd_banned_ip_t ) ); - if( !p_new_banned_ip ) - { - return -1; - } - p_new_banned_ip->p_next=NULL; - p_new_banned_ip->psz_ip = malloc( strlen( psz_new_banned_ip ) + 1 ); - if( !p_new_banned_ip->psz_ip ) - { - return -2; - } - - strcpy( p_new_banned_ip->psz_ip, psz_new_banned_ip ); - - msg_Dbg( p_httpt, "Banning IP %s", psz_new_banned_ip ); - - if( p_httpt->p_first_banned_ip ) - { - httpd_banned_ip_t *p_last; - - p_last = p_httpt->p_first_banned_ip; - while( p_last->p_next ) - { - p_last = p_last->p_next; - } - - p_last->p_next = p_new_banned_ip; - p_new_banned_ip->p_prev = p_last; - } - else - { - p_new_banned_ip->p_prev = NULL; - - p_httpt->p_first_banned_ip = p_new_banned_ip; - } - - p_httpt->i_banned_ip_count++; - return 0; -} -#endif -static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *p_httpt, char *psz_ip ) -{ - httpd_banned_ip_t *p_ip; - - p_ip = p_httpt->p_first_banned_ip; - - while( p_ip) - { - if( strcmp( psz_ip, p_ip->psz_ip ) == 0 ) - { - return p_ip; - } - p_ip = p_ip->p_next; - } - - return NULL; -} - -static int httpd_UnbanIP( httpd_sys_t *p_httpt, httpd_banned_ip_t *p_banned_ip ) -{ - if(!p_banned_ip) - { - return -1; - } - - msg_Dbg( p_httpt, "Unbanning IP %s",p_banned_ip->psz_ip); - - /* first cut out from list */ - if( p_banned_ip->p_prev ) - { - p_banned_ip->p_prev->p_next = p_banned_ip->p_next; - } - else - { - p_httpt->p_first_banned_ip = p_banned_ip->p_next; - } - - if( p_banned_ip->p_next ) - { - p_banned_ip->p_next->p_prev = p_banned_ip->p_prev; - } - - FREE( p_banned_ip->psz_ip ); - FREE( p_banned_ip ); - - p_httpt->i_banned_ip_count--; - - return 0; -} - -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 ) ); - - /* verify if it's a banned ip */ - if(httpd_GetbannedIP( p_httpt,inet_ntoa( p_sock->sin_addr ) ) ) - { - msg_Dbg( p_httpt, "Ip %s banned : closing connection", inet_ntoa( p_sock->sin_addr ) ); - close(fd); - return; - } - - /* create a new connection and link it */ - p_con = malloc( sizeof( httpd_connection_t ) ); - if( !p_con ) - { - msg_Err( p_httpt, "Out of memory" ); - return; - } - p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST; - p_con->fd = fd; - p_con->i_last_activity_date = mdate(); - - 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_request_size = 0; - p_con->p_request = NULL; - - p_con->i_buffer = 0; - p_con->i_buffer_size = 8096; - p_con->p_buffer = malloc( p_con->i_buffer_size ); - if( !p_con->p_buffer ) - { - msg_Err( p_httpt, "Out of memory"); - return ; - } - - p_con->i_stream_pos = 0; /* updated by httpd_thread */ - 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->p_request ); - 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'; 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; - int b_xplaystream = VLC_FALSE; - /* Size is checked for all of these */ - 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 ); -#if 0 - msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version ); -#endif - p_con->p_request = NULL; - p_con->i_request_size = 0; - if( !strcmp( command, "GET" ) ) - { - p_con->i_method = HTTPD_CONNECTION_METHOD_GET; - } - else if( !strcmp( command, "POST" )) - { - p_con->i_method = HTTPD_CONNECTION_METHOD_POST; - } - else if( !strcmp( command, "HEAD" )) - { - p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD; - } - else - { - /* unimplemented */ - p_con->psz_file = strdup( "/501.html" ); - p_con->i_method = HTTPD_CONNECTION_METHOD_GET; - 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 ) ) - { -#if 0 - msg_Dbg( p_httpt, "failled new line" ); -#endif - break;; - } -#if 0 - msg_Dbg( p_httpt, "new line=%s", p ); -#endif - httpd_RequestGetWord( header, 1024, &p, p_end ); - if( !strcmp( header, "\r\n" ) || !strcmp( header, "\n" ) ) - { - break; - } - - 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 ); -#if 0 - msg_Dbg( p_httpt, "Authorization: basic:%s", basic ); -#endif - b64_decode( decoded, basic ); -#if 0 - msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded ); -#endif - if( strchr( decoded, ':' ) ) - { - char *p = strchr( decoded, ':' ); - - p[0] = '\0'; p++; - strcpy( user, decoded ); - strcpy( password, p ); - } - } - } - else if( !strcmp( header, "Pragma:" ) ) - { - char method[128]; - - httpd_RequestGetWord( method, 128, &p, p_end ); - if( !strcasecmp( method, "xPlayStrm=1" ) ) - { - b_xplaystream = VLC_TRUE; - } - } - } - - if( strchr( url, '?' ) ) - { - char *p_request = strchr( url, '?' ); - *p_request++ = '\0'; - p_con->psz_file = strdup( url ); - p_con->p_request = strdup( p_request ); - p_con->i_request_size = strlen( p_con->p_request ); - } - else - { - p_con->psz_file = strdup( url ); - } - - /* fix p_request */ - if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST ) - { - char *p_request; - if( strstr( p_con->p_buffer, "\r\n\r\n" ) ) - { - p_request = strstr( p_con->p_buffer, "\r\n\r\n" ) + 4; - } - else if( strstr( p_con->p_buffer, "\n\n" ) ) - { - p_request = strstr( p_con->p_buffer, "\n\n" ) + 2; - } - else - { - p_request = NULL; - } - if( p_request && p_request < p_end ) - { - p_con->i_request_size = p_end - p_request; - p_con->p_request = malloc( p_con->i_request_size + 1); - if( !p_con->p_request) - { - msg_Err( p_httpt, "Out of memory" ); - return; - } - memcpy( p_con->p_request, - p_request, - p_con->i_request_size ); - - p_con->p_request[p_con->i_request_size] = '\0'; - } - } - p_con->i_http_error = 200; - -create_header: -#if 0 - msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error ); -#endif - FREE( p_con->p_buffer ); - p_con->i_buffer = 0; - p_con->i_buffer_size = 0; - -#if 0 - vlc_mutex_lock( &p_httpt->file_lock ); -#endif -search_file: - /* search file */ - p_con->p_file = NULL; - for( i = 0; i < p_httpt->i_file_count; i++ ) - { - /* Our strdup call failed */ - if( !p_con->psz_file ) return; - if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) ) - { - if( p_httpt->file[i]->b_stream || - p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD || - ( p_con->i_method == HTTPD_CONNECTION_METHOD_GET && p_httpt->file[i]->pf_get ) || - ( p_con->i_method == HTTPD_CONNECTION_METHOD_POST && p_httpt->file[i]->pf_post ) ) - { - p_con->p_file = p_httpt->file[i]; - break; - } - } - } - - if( !p_con->p_file ) - { - p_con->psz_file = strdup( "/404.html" ); - p_con->i_http_error = 404; - p_con->i_method = HTTPD_CONNECTION_METHOD_GET; - - /* 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; - p_con->i_method = HTTPD_CONNECTION_METHOD_GET; - - /* XXX do not put password on 404 else ... */ - goto search_file; - } - } - if( !strcmp( p_con->p_file->psz_mime, "video/x-ms-asf-stream" ) && - p_con->i_method == HTTPD_CONNECTION_METHOD_POST ) - { - p_con->psz_file = strdup( "/400.html" ); - p_con->i_http_error = 400; - p_con->i_method = HTTPD_CONNECTION_METHOD_GET; - - goto search_file; - } - - p_con->p_file->i_ref++; -#if 0 - vlc_mutex_unlock( &p_httpt->file_lock ); -#endif - - switch( p_con->i_http_error ) - { - case 200: - psz_status = "OK"; - break; - case 400: - psz_status = "Bad Request"; - 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; - - /* we send stream header with this one */ - if( p_con->i_http_error == 200 && p_con->p_file->b_stream ) - { - p_con->i_buffer_size += p_con->p_file->i_header_size; - } - - p = p_con->p_buffer = malloc( p_con->i_buffer_size ); - if( !p) - { - msg_Err( p_httpt, "Out of memory" ); - return; - } - p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status ); - - /* Special mmsh case cludgy but ...*/ - if( !strcmp( p_con->p_file->psz_mime, "video/x-ms-asf-stream" ) && - p_con->i_method == HTTPD_CONNECTION_METHOD_GET ) - { - p += sprintf( p, "Content-type: application/octet-stream\r\n" ); - p += sprintf( p, "Server: Cougar 4.1.0.3921\r\n" ); - p += sprintf( p, "Cache-Control: no-cache\r\n" ); - p += sprintf( p, "Pragma: no-cache\r\n" ); - p += sprintf( p, "Pragma: client-id=%d\r\n", rand()&0x7fff ); - p += sprintf( p, "Pragma: features=\"broadcast\"\r\n" ); - - if( !b_xplaystream ) - { - p_con->i_method = HTTPD_CONNECTION_METHOD_ASFHEAD; - } - } - else - { - p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime ); - p += sprintf( p, "Cache-Control: no-cache\r\n" ); - } - if( p_con->i_http_error == 401 ) - { - p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user ); - } -#if 0 - if( p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD ) - { - p += sprintf( p, "Content-Length: 0\r\n" ); - } - else if( p_con->i_method == HTTPD_CONNECTION_METHOD_ASFHEAD && - p_con->p_file->i_header_size > 0 ) - { - p += sprintf( p, "Content-Length: %d\r\n", p_con->p_file->i_header_size ); - } -#endif - p += sprintf( p, "\r\n" ); - - p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1; - - if( p_con->i_http_error == 200 && - p_con->i_method != HTTPD_CONNECTION_METHOD_HEAD && - p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 ) - { - /* add stream header */ - memcpy( &p_con->p_buffer[p_con->i_buffer_size], - p_con->p_file->p_header, - p_con->p_file->i_header_size ); - p_con->i_buffer_size += p_con->p_file->i_header_size; - } - - if( p_con->i_method == HTTPD_CONNECTION_METHOD_ASFHEAD ) - { - p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD; - } -#if 0 - msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer ); -#endif -} -#define HTTPD_STREAM_PACKET 10000 -static void httpd_Thread( httpd_sys_t *p_httpt ) -{ - httpd_file_t *p_page_400; - httpd_file_t *p_page_401; - httpd_file_t *p_page_404; - - httpd_connection_t *p_con; - - msg_Info( p_httpt, "httpd started" ); - - p_page_400 = _RegisterFile( p_httpt, - "/400.html", "text/html", - NULL, NULL, - httpd_page_400_get, - NULL, - (httpd_file_callback_args_t*)NULL ); - - p_page_401 = _RegisterFile( p_httpt, - "/401.html", "text/html", - NULL, NULL, - httpd_page_401_get, - NULL, - (httpd_file_callback_args_t*)NULL ); - p_page_404 = _RegisterFile( p_httpt, - "/404.html", "text/html", - NULL, NULL, - httpd_page_404_get, - NULL, - (httpd_file_callback_args_t*)NULL ); - - while( !p_httpt->b_die ) - { - struct timeval timeout; - fd_set fds_read; - fd_set fds_write; - int i_handle_max = 0; - int i_ret; - int i; - if( p_httpt->i_host_count <= 0 ) - { - msleep( 100 * 1000 ); - continue; - } - - /* we will create a socket set with host and connection */ - FD_ZERO( &fds_read ); - FD_ZERO( &fds_write ); - - vlc_mutex_lock( &p_httpt->host_lock ); - vlc_mutex_lock( &p_httpt->connection_lock ); - for( i = 0; i < p_httpt->i_host_count; i++ ) - { - FD_SET( p_httpt->host[i]->fd, &fds_read ); - i_handle_max = __MAX( i_handle_max, p_httpt->host[i]->fd ); - } - for( p_con = p_httpt->p_first_connection; p_con != NULL; ) - { - /* no more than 10s of inactivity */ - if( p_con->i_last_activity_date + (mtime_t)HTTPD_CONNECTION_MAX_UNUSED < mdate() || - p_con->i_state == HTTPD_CONNECTION_TO_BE_CLOSED) - { - httpd_connection_t *p_next = p_con->p_next; - - msg_Dbg( p_httpt, "close unused connection" ); - httpd_ConnnectionClose( p_httpt, p_con ); - p_con = p_next; - continue; - } - - if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM && p_con->i_stream_pos + HTTPD_STREAM_PACKET >= p_con->p_file->i_buffer_pos ) - { - p_con = p_con->p_next; - continue; - } - - if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST ) - { - FD_SET( p_con->fd, &fds_read ); - } - else - { - FD_SET( p_con->fd, &fds_write ); - } - i_handle_max = __MAX( i_handle_max, p_con->fd ); - - p_con = p_con->p_next; - } - vlc_mutex_unlock( &p_httpt->host_lock ); - vlc_mutex_unlock( &p_httpt->connection_lock ); - - /* we will wait 0.5s */ - timeout.tv_sec = 0; - timeout.tv_usec = 500*1000; - - i_ret = select( i_handle_max + 1, - &fds_read, - &fds_write, - NULL, - &timeout ); - if( i_ret == -1 && errno != EINTR ) - { - msg_Warn( p_httpt, "cannot select sockets" ); - msleep( 1000 ); - continue; - } - - if( i_ret <= 0 ) - { - 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, (struct sockaddr *)&sock, - &i_sock_size ); - if( fd > 0 ) - { -#if defined( WIN32 ) || defined( UNDER_CE ) - { - unsigned long i_dummy = 1; - ioctlsocket( fd, FIONBIO, &i_dummy ); - } -#else - fcntl( fd, F_SETFL, O_NONBLOCK ); -#endif - - 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 defined( WIN32 ) || defined( UNDER_CE ) - if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) ) -#else - if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) ) -#endif - { - 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_last_activity_date = mdate(); - 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 */ - if( p_con->i_buffer_size - p_con->i_buffer > 0 ) - { - i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 ); - } - else - { - i_len = 0; - } -#if 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 ); -#endif - -#if defined( WIN32 ) || defined( UNDER_CE ) - if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) ) -#else - if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) ) -#endif - { - 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_last_activity_date = mdate(); - 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_method == HTTPD_CONNECTION_METHOD_HEAD ) - { - p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; /* be sure to out from HTTPD_CONNECTION_SENDING_HEADER */ - if( p_con->i_method == HTTPD_CONNECTION_METHOD_GET ) - { - p_con->p_file->pf_get( p_con->p_file->p_sys, - p_con->p_request, p_con->i_request_size, - &p_con->p_buffer, &p_con->i_buffer_size ); - } - else if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST ) - { - p_con->p_file->pf_post( p_con->p_file->p_sys, - p_con->p_request, p_con->i_request_size, - &p_con->p_buffer, &p_con->i_buffer_size ); - } - else - { - /* HTTPD_CONNECTION_METHOD_HEAD for example */ - p_con->p_buffer = NULL; - p_con->i_buffer_size = 0; - } - } - else - { - p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM; - p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos; - } - 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_stream_t *p_stream = p_con->p_file; - int i_send; - int i_write; - - if( p_con->i_stream_pos < p_stream->i_buffer_pos ) - { - int i_pos; - /* check if this p_con aren't to late */ - if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos ) - { - fprintf( stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n", - p_con->i_stream_pos, p_stream->i_buffer_pos ); - p_con->i_stream_pos = p_stream->i_buffer_last_pos; - } - - i_pos = p_con->i_stream_pos % p_stream->i_buffer_size; - /* size until end of buffer */ - i_write = p_stream->i_buffer_size - i_pos; - /* is it more than valid data */ - if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos ) - { - i_write = p_stream->i_buffer_pos - p_con->i_stream_pos; - } - /* limit to HTTPD_STREAM_PACKET */ - if( i_write > HTTPD_STREAM_PACKET ) - { - i_write = HTTPD_STREAM_PACKET; - } - i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 ); - -#if defined( WIN32 ) || defined( UNDER_CE ) - if( ( i_send < 0 && WSAGetLastError() != WSAEWOULDBLOCK )|| ( i_send == 0 ) ) -#else - if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )|| ( i_send == 0 ) ) -#endif - { - httpd_connection_t *p_next = p_con->p_next; - - httpd_ConnnectionClose( p_httpt, p_con ); - p_con = p_next; - continue; - } - else if( i_send > 0 ) - { - p_con->i_last_activity_date = mdate(); - p_con->i_stream_pos += i_send; - } - } - p_con = p_con->p_next; - continue; /* just for clarity */ - } - else if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED ) - { - msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" ); - p_con = p_con->p_next; - } - } /* for over connection */ - - vlc_mutex_unlock( &p_httpt->file_lock ); - } - - msg_Info( p_httpt, "httpd stopped" ); - - _UnregisterFile( p_httpt, p_page_400 ); - _UnregisterFile( p_httpt, p_page_401 ); - _UnregisterFile( p_httpt, p_page_404 ); -} diff --git a/src/misc/httpd.c b/src/misc/httpd.c new file mode 100644 index 0000000000..d758110fb8 --- /dev/null +++ b/src/misc/httpd.c @@ -0,0 +1,2125 @@ +/***************************************************************************** + * httpd.c + ***************************************************************************** + * Copyright (C) 2004 VideoLAN + * $Id: httpd.c,v 1.1 2004/03/03 13:23:47 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. + *****************************************************************************/ + +#include +#include + +#include "vlc_httpd.h" + +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include + +#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 + +#if 0 +typedef struct httpd_t httpd_t; + +typedef struct httpd_host_t httpd_host_t; +typedef struct httpd_url_t httpd_url_t; +typedef struct httpd_client_t httpd_client_t; + +enum +{ + HTTPD_MSG_NONE, + + /* answer */ + HTTPD_MSG_ANSWER, + + /* channel communication */ + HTTPD_MSG_CHANNEL, + + /* http request */ + HTTPD_MSG_GET, + HTTPD_MSG_HEAD, + HTTPD_MSG_POST, + + /* rtsp request */ + HTTPD_MSG_OPTIONS, + HTTPD_MSG_DESCRIBE, + HTTPD_MSG_SETUP, + HTTPD_MSG_PLAY, + HTTPD_MSG_PAUSE, + HTTPD_MSG_TEARDOWN, + + /* just to track the count of MSG */ + HTTPD_MSG_MAX +}; + +enum +{ + HTTPD_PROTO_NONE, + HTTPD_PROTO_HTTP, + HTTPD_PROTO_RTSP, +}; + +typedef struct +{ + httpd_client_t *cl; /* NULL if not throught a connection e vlc internal */ + + int i_type; + int i_proto; + int i_version; + + /* for an answer */ + int i_status; + char *psz_status; + + /* for a query */ + char *psz_url; + char *psz_args; /* FIXME find a clean way to handle GET(psz_args) and POST(body) through the same code */ + + /* for rtp over rtsp */ + int i_channel; + + /* options */ + int i_name; + char **name; + int i_value; + char **value; + + /* body */ + int64_t i_body_offset; + int i_body; + uint8_t *p_body; + +} httpd_message_t; + +typedef struct httpd_callback_sys_t httpd_callback_sys_t; +/* answer could be null, int this case no answer is requested */ +typedef int (*httpd_callback_t)( httpd_callback_sys_t *, httpd_client_t *, httpd_message_t *answer, httpd_message_t *query ); + + +/* create a new host */ +httpd_host_t *httpd_HostNew( vlc_object_t *, char *psz_host, int i_port ); +/* delete a host */ +void httpd_HostDelete( httpd_host_t * ); + +/* register a new url */ +httpd_url_t *httpd_UrlNew( httpd_host_t *, char *psz_url ); +/* register callback on a url */ +int httpd_UrlCatch( httpd_url_t *, int i_msg, + httpd_callback_t, httpd_callback_sys_t * ); +/* delete an url */ +void httpd_UrlDelete( httpd_url_t * ); + + +void httpd_ClientModeStream( httpd_client_t *cl ); +void httpd_ClientModeBidir( httpd_client_t *cl ); + +/* High level */ +typedef struct httpd_file_t httpd_file_t; +typedef struct httpd_file_sys_t httpd_file_sys_t; +typedef int (*httpd_file_callback_t)( httpd_file_sys_t*, httpd_file_t *, uint8_t *psz_request, uint8_t **pp_data, int *pi_data ); +httpd_file_t *httpd_FileNew( httpd_host_t *, + char *psz_url, char *psz_mime, + char *psz_user, char *psz_password, + httpd_file_callback_t pf_fill, + httpd_file_sys_t *p_sys ); +void httpd_FileDelete( httpd_file_t * ); + +typedef struct httpd_redirect_t httpd_redirect_t; +httpd_redirect_t *httpd_RedirectNew( httpd_host_t *, char *psz_url_dst, char *psz_url_src ); +void httpd_RedirectDelete( httpd_redirect_t * ); + +#if 0 +typedef struct httpd_stream_t httpd_stream_t; +httpd_stream_t *httpd_StreamNew( httpd_host_t * ); +int httpd_StreamHeader( httpd_stream_t *, uint8_t *p_data, int i_data ); +int httpd_StreamSend( httpd_stream_t *, uint8_t *p_data, int i_data ); +void httpd_StreamDelete( httpd_stream_t * ); +#endif + +/* Msg functions facilities */ +void httpd_MsgInit( httpd_message_t * ); +void httpd_MsgAdd( httpd_message_t *, char *psz_name, char *psz_value, ... ); +/* return "" if not found. The string is not allocated */ +char *httpd_MsgGet( httpd_message_t *, char *psz_name ); +void httpd_MsgClean( httpd_message_t * ); +#endif + +#if 0 +struct httpd_t +{ + VLC_COMMON_MEMBERS + + /* vlc_mutex_t lock; */ + int i_host; + httpd_host_t **host; +}; +#endif + +/* each host run in his own thread */ +struct httpd_host_t +{ + VLC_COMMON_MEMBERS + + httpd_t *httpd; + + /* ref count */ + int i_ref; + + /* address/port and socket for listening at connections */ + struct sockaddr_in sock; + int fd; + + vlc_mutex_t lock; + + /* all registered url (becarefull that 2 httpd_url_t could point at the same url) + * This will slow down the url research but make my live easier + * All url will have their cb trigger, but only the first one can answer + * */ + int i_url; + httpd_url_t **url; + + int i_client; + httpd_client_t **client; +}; + +struct httpd_url_t +{ + httpd_host_t *host; + + vlc_mutex_t lock; + + char *psz_url; + char *psz_user; + char *psz_password; + + struct + { + httpd_callback_t cb; + httpd_callback_sys_t *p_sys; + } catch[HTTPD_MSG_MAX]; +}; + +/* status */ +enum +{ + HTTPD_CLIENT_RECEIVING, + HTTPD_CLIENT_RECEIVE_DONE, + + HTTPD_CLIENT_SENDING, + HTTPD_CLIENT_SEND_DONE, + + HTTPD_CLIENT_WAITING, + + HTTPD_CLIENT_DEAD, +}; +/* mode */ +enum +{ + HTTPD_CLIENT_FILE, /* default */ + HTTPD_CLIENT_STREAM, /* regulary get data from cb */ + HTTPD_CLIENT_BIDIR, /* check for reading and get data from cb */ +}; + +struct httpd_client_t +{ + httpd_url_t *url; + + int i_ref; + + struct sockaddr_in sock; + int fd; + + int i_mode; + int i_state; + int b_read_waiting; /* stop as soon as possible sending */ + + mtime_t i_activity_date; + mtime_t i_activity_timeout; + + /* buffer for reading header */ + int i_buffer_size; + int i_buffer; + uint8_t *p_buffer; + + /* */ + httpd_message_t query; /* client -> httpd */ + httpd_message_t answer; /* httpd -> client */ +}; + + +/***************************************************************************** + * Various functions + *****************************************************************************/ +/*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'; src++ ) + { + int c; + + c = b64[(unsigned int)*src]; + if( c == -1 ) + { + 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 struct +{ + char *psz_ext; + char *psz_mime; +} http_mime[] = +{ + { ".htm", "text/html" }, + { ".html", "text/html" }, + { ".txt", "text/plain" }, + { ".xml", "text/xml" }, + { ".dtd", "text/dtd" }, + + { ".css", "text/css" }, + + /* image mime */ + { ".gif", "image/gif" }, + { ".jpe", "image/jpeg" }, + { ".jpg", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, + { ".png", "image/png" }, + + /* media mime */ + { ".avi", "video/avi" }, + { ".asf", "video/x-ms-asf" }, + { ".m1a", "audio/mpeg" }, + { ".m2a", "audio/mpeg" }, + { ".m1v", "video/mpeg" }, + { ".m2v", "video/mpeg" }, + { ".mp2", "audio/mpeg" }, + { ".mp3", "audio/mpeg" }, + { ".mpa", "audio/mpeg" }, + { ".mpg", "video/mpeg" }, + { ".mpeg", "video/mpeg" }, + { ".mpe", "video/mpeg" }, + { ".mov", "video/quicktime" }, + { ".moov", "video/quicktime" }, + { ".ogg", "application/ogg" }, + { ".ogm", "application/ogg" }, + { ".wav", "audio/wav" }, + { ".wma", "audio/x-ms-wma" }, + { ".wmv", "video/x-ms-wmv" }, + + + /* end */ + { NULL, NULL } +}; +static char *httpd_MimeFromUrl( char *psz_url ) +{ + + char *psz_ext; + + psz_ext = strrchr( psz_url, '.' ); + if( psz_ext ) + { + int i; + + for( i = 0; http_mime[i].psz_ext != NULL ; i++ ) + { + if( !strcasecmp( http_mime[i].psz_ext, psz_ext ) ) + { + return http_mime[i].psz_mime; + } + } + } + return "application/octet-stream"; +} + +/***************************************************************************** + * High Level Funtions: httpd_file_t + *****************************************************************************/ +struct httpd_file_t +{ + httpd_url_t *url; + + char *psz_url; + char *psz_mime; + + httpd_file_callback_t pf_fill; + httpd_file_sys_t *p_sys; + +}; + + +static int httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query ) +{ + httpd_file_t *file = (httpd_file_t*)p_sys; + + if( answer == NULL || query == NULL ) + { + return VLC_SUCCESS; + } + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= query->i_version; + answer->i_type = HTTPD_MSG_ANSWER; + + answer->i_status = 200; + answer->psz_status = strdup( "OK" ); + + httpd_MsgAdd( answer, "Content-type", "%s", file->psz_mime ); + httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + + if( query->i_type != HTTPD_MSG_HEAD ) + { + char *psz_args = query->psz_args; + if( query->i_type == HTTPD_MSG_POST ) + { + /* Check that */ + psz_args = query->p_body; + } + file->pf_fill( file->p_sys, file, psz_args, &answer->p_body, &answer->i_body ); + } + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + return VLC_SUCCESS; +} + + +httpd_file_t *httpd_FileNew( httpd_host_t *host, + char *psz_url, char *psz_mime, + char *psz_user, char *psz_password, + httpd_file_callback_t pf_fill, + httpd_file_sys_t *p_sys ) +{ + httpd_file_t *file = malloc( sizeof( httpd_file_t ) ); + + if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user, psz_password ) ) == NULL ) + { + free( file ); + return NULL; + } + + file->psz_url = strdup( psz_url ); + if( psz_mime && *psz_mime ) + { + file->psz_mime = strdup( psz_mime ); + } + else + { + file->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) ); + } + + file->pf_fill = pf_fill; + file->p_sys = p_sys; + + httpd_UrlCatch( file->url, HTTPD_MSG_HEAD, httpd_FileCallBack, (httpd_callback_sys_t*)file ); + httpd_UrlCatch( file->url, HTTPD_MSG_GET, httpd_FileCallBack, (httpd_callback_sys_t*)file ); + httpd_UrlCatch( file->url, HTTPD_MSG_POST, httpd_FileCallBack, (httpd_callback_sys_t*)file ); + + return file; +} + +void httpd_FileDelete( httpd_file_t *file ) +{ + httpd_UrlDelete( file->url ); + + free( file->psz_url ); + free( file->psz_mime ); + + free( file ); +} + +/***************************************************************************** + * High Level Funtions: httpd_redirect_t + *****************************************************************************/ +struct httpd_redirect_t +{ + httpd_url_t *url; + char *psz_dst; +}; + +static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query ) +{ + httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys; + uint8_t *p; + + if( answer == NULL || query == NULL ) + { + return VLC_SUCCESS; + } + answer->i_proto = query->i_proto; + answer->i_version= query->i_version; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_status = 301; + answer->psz_status = strdup( "Moved Permanently" ); + + p = answer->p_body = malloc( 1000 + strlen( rdir->psz_dst ) ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "Redirection\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "

You should be redirected

\n", rdir->psz_dst ); + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + answer->i_body = p - answer->p_body; + + /* XXX check if it's ok or we need to set an absolute url */ + httpd_MsgAdd( answer, "Location", "%s", rdir->psz_dst ); + + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + return VLC_SUCCESS; +} + +httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, char *psz_url_dst, char *psz_url_src ) +{ + httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) ); + + if( ( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL ) ) == NULL ) + { + free( rdir ); + return NULL; + } + rdir->psz_dst = strdup( psz_url_dst ); + /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */ + httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir ); + httpd_UrlCatch( rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir ); + httpd_UrlCatch( rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir ); + + httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir ); + + return rdir; +} +void httpd_RedirectDelete( httpd_redirect_t *rdir ) +{ + httpd_UrlDelete( rdir->url ); + free( rdir->psz_dst ); + free( rdir ); +} + +/***************************************************************************** + * High Level Funtions: httpd_stream_t + *****************************************************************************/ +struct httpd_stream_t +{ + vlc_mutex_t lock; + httpd_url_t *url; + + char *psz_mime; + + /* Header to send as first packet */ + uint8_t *p_header; + int i_header; + + /* circular buffer */ + int i_buffer_size; /* buffer size, can't be reallocated smaller */ + uint8_t *p_buffer; /* buffer */ + int64_t i_buffer_pos; /* absolute position from begining */ + int64_t i_buffer_last_pos; /* a new connection will start with that */ +}; + +static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query ) +{ + httpd_stream_t *stream = (httpd_stream_t*)p_sys; + + if( answer == NULL || query == NULL || cl == NULL ) + { + return VLC_SUCCESS; + } + if( answer->i_body_offset > 0 ) + { + int64_t i_write; + int i_pos; + + /* fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n", answer->i_body_offset ); */ + + if( answer->i_body_offset >= stream->i_buffer_pos ) + { + /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */ + return VLC_EGENERIC; /* wait, no data available */ + } + if( answer->i_body_offset + stream->i_buffer_size < stream->i_buffer_pos ) + { + /* this client isn't fast enough */ + fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n", + answer->i_body_offset, stream->i_buffer_last_pos ); + answer->i_body_offset = stream->i_buffer_last_pos; + } + + i_pos = answer->i_body_offset % stream->i_buffer_size; + i_write = stream->i_buffer_pos - answer->i_body_offset; + if( i_write > 10000 ) + { + i_write = 10000; + } + else if( i_write <= 0 ) + { + return VLC_EGENERIC; /* wait, no data available */ + } + + /* using HTTPD_MSG_ANSWER -> data available */ + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= 0; + answer->i_type = HTTPD_MSG_ANSWER; + + answer->i_body = i_write; + answer->p_body = malloc( i_write ); + memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write ); + + answer->i_body_offset += i_write; + + return VLC_SUCCESS; + } + else + { + answer->i_proto = HTTPD_PROTO_HTTP; + answer->i_version= 0; + answer->i_type = HTTPD_MSG_ANSWER; + + answer->i_status = 200; + answer->psz_status = strdup( "OK" ); + + if( query->i_type != HTTPD_MSG_HEAD ) + { + httpd_ClientModeStream( cl ); + vlc_mutex_lock( &stream->lock ); + /* Send the header */ + if( stream->i_header > 0 ) + { + answer->i_body = stream->i_header; + answer->p_body = malloc( stream->i_header ); + memcpy( answer->p_body, stream->p_header, stream->i_header ); + } + answer->i_body_offset = stream->i_buffer_last_pos; + vlc_mutex_unlock( &stream->lock ); + } + else + { + httpd_MsgAdd( answer, "Content-Length", "%d", 0 ); + } + + if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) ) + { + vlc_bool_t b_xplaystream = VLC_FALSE; + int i; + + httpd_MsgAdd( answer, "Content-type", "%s", "application/octet-stream" ); + httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" ); + httpd_MsgAdd( answer, "Pragma", "no-cache" ); + httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff ); + httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" ); + + /* Check if there is a xPlayStrm=1 */ + for( i = 0; i < query->i_name; i++ ) + { + if( !strcasecmp( query->name[i], "Pragma" ) && + !strcasecmp( query->value[i], "xPlayStrm=1" ) ) + { + b_xplaystream = VLC_TRUE; + } + } + + if( !b_xplaystream ) + { + answer->i_body_offset = 0; + } + } + else + { + httpd_MsgAdd( answer, "Content-type", "%s", stream->psz_mime ); + } + httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" ); + return VLC_SUCCESS; + } +} + +httpd_stream_t *httpd_StreamNew( httpd_host_t *host, + char *psz_url, char *psz_mime, + char *psz_user, char *psz_password ) +{ + httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) ); + + if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user, psz_password ) ) == NULL ) + { + free( stream ); + return NULL; + } + vlc_mutex_init( host, &stream->lock ); + if( psz_mime && *psz_mime ) + { + stream->psz_mime = strdup( psz_mime ); + } + else + { + stream->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) ); + } + stream->i_header = 0; + stream->p_header = NULL; + stream->i_buffer_size = 5000000; /* 5 Mo per stream */ + stream->p_buffer = malloc( stream->i_buffer_size ); + /* We set to 1, to make life simpler (this way i_body_offset can never be 0) */ + stream->i_buffer_pos = 1; + stream->i_buffer_last_pos = 1; + + httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack, (httpd_callback_sys_t*)stream ); + httpd_UrlCatch( stream->url, HTTPD_MSG_GET, httpd_StreamCallBack, (httpd_callback_sys_t*)stream ); + httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack, (httpd_callback_sys_t*)stream ); + + return stream; +} + +int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data ) +{ + vlc_mutex_lock( &stream->lock ); + if( stream->p_header ) + { + free( stream->p_header ); + stream->p_header = NULL; + } + stream->i_header = i_data; + if( i_data > 0 ) + { + stream->p_header = malloc( i_data ); + memcpy( stream->p_header, p_data, i_data ); + } + vlc_mutex_unlock( &stream->lock ); + + return VLC_SUCCESS; +} + +int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data ) +{ + int i_count; + int i_pos; + + if( i_data < 0 || p_data == NULL ) + { + return VLC_SUCCESS; + } + vlc_mutex_lock( &stream->lock ); + + /* save this pointer (to be used by new connection) */ + stream->i_buffer_last_pos = stream->i_buffer_pos; + + i_pos = stream->i_buffer_pos % stream->i_buffer_size; + i_count = i_data; + while( i_count > 0) + { + int i_copy; + + i_copy = __MIN( i_count, stream->i_buffer_size - i_pos ); + + /* Ok, we can't go past the end of our buffer */ + memcpy( &stream->p_buffer[i_pos], p_data, i_copy ); + + i_pos = ( i_pos + i_copy ) % stream->i_buffer_size; + i_count -= i_copy; + p_data += i_copy; + } + + stream->i_buffer_pos += i_data; + + vlc_mutex_unlock( &stream->lock ); + return VLC_SUCCESS; +} + +void httpd_StreamDelete( httpd_stream_t *stream ) +{ + httpd_UrlDelete( stream->url ); + vlc_mutex_destroy( &stream->lock ); + if( stream->psz_mime ) free( stream->psz_mime ); + if( stream->p_header ) free( stream->p_header ); + if( stream->p_buffer ) free( stream->p_buffer ); + free( stream ); +} + + +/***************************************************************************** + * Low level + *****************************************************************************/ +#define LISTEN_BACKLOG 100 + +#if defined( WIN32 ) || defined( UNDER_CE ) +#define SOCKET_CLOSE(a) closesocket(a) +#else +#define SOCKET_CLOSE(a) close(a) +#endif + +static void httpd_HostThread( httpd_host_t * ); +static int BuildAddr( struct sockaddr_in * p_socket, + const char * psz_address, int i_port ); + + +/* create a new host */ +httpd_host_t *httpd_HostNew( vlc_object_t *p_this, char *psz_host, int i_port ) +{ + httpd_t *httpd; + httpd_host_t *host; + vlc_value_t lockval; + struct sockaddr_in sock; + int i; + + /* resolv */ + if( BuildAddr( &sock, psz_host, i_port ) ) + { + msg_Err( p_this, "cannot build address for %s:%d", psz_host, i_port ); + return NULL; + } + + /* to be sure to avoid multiple creation */ + var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX ); + var_Get( p_this->p_libvlc, "httpd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + if( ( httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE ) ) == NULL ) + { + msg_Info( p_this, "creating httpd" ); + if( ( httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ) ) == NULL ) + { + vlc_mutex_unlock( lockval.p_address ); + return NULL; + } + + httpd->i_host = 0; + httpd->host = NULL; + + vlc_object_yield( httpd ); + vlc_object_attach( httpd, p_this->p_vlc ); + } + + /* verify if it already exist */ + for( i = 0; i < httpd->i_host; i++ ) + { + if( httpd->host[i]->sock.sin_port == sock.sin_port && + ( httpd->host[i]->sock.sin_addr.s_addr == INADDR_ANY || + httpd->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr ) ) + { + /* yep found */ + host = httpd->host[i]; + host->i_ref++; + + vlc_mutex_unlock( lockval.p_address ); + + msg_Dbg( p_this, "host already registered" ); + return host; + } + } + /* create the new host */ + host = vlc_object_create( p_this, sizeof( httpd_host_t ) ); + host->httpd = httpd; + vlc_mutex_init( httpd, &host->lock ); + host->i_ref = 1; + memcpy( &host->sock, &sock, sizeof( struct sockaddr_in ) ); + host->i_url = 0; + host->url = NULL; + host->i_client = 0; + host->client = NULL; + + /* create the listening socket */ + if( ( host->fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) + { + goto socket_error; + } + /* reuse socket */ + i = 1; + if( setsockopt( host->fd, SOL_SOCKET, SO_REUSEADDR, + (void *) &i, sizeof( i ) ) < 0 ) + { + msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" ); + } + /* bind it */ + if( bind( host->fd, (struct sockaddr *)&host->sock, sizeof( struct sockaddr_in ) ) < 0 ) + { + msg_Err( p_this, "cannot bind socket" ); + goto socket_error; + } + /* set to non-blocking */ +#if defined( WIN32 ) || defined( UNDER_CE ) + { + unsigned long i_dummy = 1; + if( ioctlsocket( host->fd, FIONBIO, &i_dummy ) != 0 ) + { + msg_Err( p_this, "cannot set socket to non-blocking mode" ); + goto socket_error; + } + } +#else + { + unsigned int i_flags; + if( ( i_flags = fcntl( host->fd, F_GETFL, 0 ) ) < 0 ) + { + msg_Err( p_this, "cannot F_GETFL socket" ); + goto socket_error; + } + if( fcntl( host->fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 ) + { + msg_Err( p_this, "cannot F_SETFL O_NONBLOCK" ); + goto socket_error; + } + } +#endif + /* listen */ + if( listen( host->fd, LISTEN_BACKLOG ) < 0 ) + { + msg_Err( p_this, "cannot listen socket" ); + goto socket_error; + } + + /* create the thread */ + if( vlc_thread_create( host, "httpd host thread", + httpd_HostThread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) ) + { + msg_Err( p_this, "cannot spawn http host thread" ); + goto socket_error; + } + + /* now add it to httpd */ + TAB_APPEND( httpd->i_host, httpd->host, host ); + vlc_mutex_unlock( lockval.p_address ); + + return host; + +socket_error: + vlc_mutex_unlock( lockval.p_address ); + + if( host->fd > 0 ) + { + SOCKET_CLOSE( host->fd ); + } + vlc_mutex_destroy( &host->lock ); + vlc_object_destroy( host ); + + /* TODO destroy no more used httpd TODO */ + vlc_object_release( httpd ); + return NULL; +} + +/* delete a host */ +void httpd_HostDelete( httpd_host_t *host ) +{ + httpd_t *httpd = host->httpd; + vlc_value_t lockval; + int i; + + msg_Dbg( host, "httpd_HostDelete" ); + + var_Get( httpd->p_libvlc, "httpd_mutex", &lockval ); + vlc_mutex_lock( lockval.p_address ); + + vlc_object_release( httpd ); + + host->i_ref--; + if( host->i_ref > 0 ) + { + /* still used */ + vlc_mutex_unlock( lockval.p_address ); + msg_Dbg( host, "httpd_HostDelete: host still used" ); + return; + } + TAB_REMOVE( httpd->i_host, httpd->host, host ); + + msg_Dbg( host, "httpd_HostDelete: host removed from http" ); + + host->b_die = 1; + vlc_thread_join( host ); + + msg_Dbg( host, "httpd_HostDelete: host thread joined" ); + + for( i = 0; i < host->i_url; i++ ) + { + msg_Err( host, "url still registered:%s", host->url[i]->psz_url ); + } + for( i = 0; i < host->i_client; i++ ) + { + httpd_client_t *cl = host->client[i]; + msg_Warn( host, "client still connected" ); + SOCKET_CLOSE( cl->fd ); + /* TODO */ + } + + SOCKET_CLOSE( host->fd ); + vlc_mutex_destroy( &host->lock ); + vlc_object_destroy( host ); + + if( httpd->i_host <= 0 ) + { + msg_Info( httpd, "httpd doesn't reference any host, deleting" ); + vlc_object_detach( httpd ); + vlc_object_destroy( httpd ); + } + vlc_mutex_unlock( lockval.p_address ); +} + +/* register a new url */ +static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password, vlc_bool_t b_check ) +{ + httpd_url_t *url; + int i; + + vlc_mutex_lock( &host->lock ); + if( b_check ) + { + for( i = 0; i < host->i_url; i++ ) + { + if( !strcmp( psz_url, host->url[i]->psz_url ) ) + { + msg_Warn( host->httpd, + "cannot add '%s' (url already defined)", psz_url ); + vlc_mutex_unlock( &host->lock ); + return NULL; + } + } + } + + url = malloc( sizeof( httpd_url_t ) ); + url->host = host; + + vlc_mutex_init( host->httpd, &url->lock ); + url->psz_url = strdup( psz_url ); + url->psz_user = strdup( psz_user ? psz_user : "" ); + url->psz_password = strdup( psz_password ? psz_password : "" ); + for( i = 0; i < HTTPD_MSG_MAX; i++ ) + { + url->catch[i].cb = NULL; + url->catch[i].p_sys = NULL; + } + + TAB_APPEND( host->i_url, host->url, url ); + vlc_mutex_unlock( &host->lock ); + + return url; +} + +httpd_url_t *httpd_UrlNew( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password ) +{ + return httpd_UrlNewPrivate( host, psz_url, psz_user, psz_password, VLC_FALSE ); +} + +httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password ) +{ + return httpd_UrlNewPrivate( host, psz_url, psz_user, psz_password, VLC_TRUE ); +} + +/* register callback on a url */ +int httpd_UrlCatch( httpd_url_t *url, int i_msg, + httpd_callback_t cb, + httpd_callback_sys_t *p_sys ) +{ + vlc_mutex_lock( &url->lock ); + url->catch[i_msg].cb = cb; + url->catch[i_msg].p_sys= p_sys; + vlc_mutex_unlock( &url->lock ); + + return VLC_SUCCESS; +} + + +/* delete an url */ +void httpd_UrlDelete( httpd_url_t *url ) +{ + httpd_host_t *host = url->host; + int i; + + vlc_mutex_lock( &host->lock ); + TAB_REMOVE( host->i_url, host->url, url ); + + vlc_mutex_destroy( &url->lock ); + free( url->psz_url ); + free( url->psz_user ); + free( url->psz_password ); + + for( i = 0; i < host->i_client; i++ ) + { + httpd_client_t *client = host->client[i]; + + if( client->url == url ) + { + /* TODO complete it */ + msg_Warn( host, "force closing connections" ); + SOCKET_CLOSE( client->fd ); + TAB_REMOVE( host->i_client, host->client, client ); + i--; + } + } + vlc_mutex_unlock( &host->lock ); +} + +void httpd_MsgInit( httpd_message_t *msg ) +{ + msg->cl = NULL; + msg->i_type = HTTPD_MSG_NONE; + msg->i_proto = HTTPD_PROTO_NONE; + msg->i_version = -1; + + msg->i_status = 0; + msg->psz_status = NULL; + + msg->psz_url = NULL; + msg->psz_args = NULL; + + msg->i_channel = -1; + + msg->i_name = 0; + msg->name = NULL; + msg->i_value= 0; + msg->value = NULL; + + msg->i_body_offset = 0; + msg->i_body = 0; + msg->p_body = 0; +} + +void httpd_MsgClean( httpd_message_t *msg ) +{ + int i; + + if( msg->psz_status ) + { + free( msg->psz_status ); + } + if( msg->psz_url ) + { + free( msg->psz_url ); + } + if( msg->psz_args ) + { + free( msg->psz_args ); + } + for( i = 0; i < msg->i_name; i++ ) + { + free( msg->name[i] ); + free( msg->value[i] ); + } + if( msg->name ) + { + free( msg->name ); + } + if( msg->value ) + { + free( msg->value ); + } + if( msg->p_body ) + { + free( msg->p_body ); + } + httpd_MsgInit( msg ); +} + +char *httpd_MsgGet( httpd_message_t *msg, char *name ) +{ + int i; + + for( i = 0; i < msg->i_name; i++ ) + { + if( !strcasecmp( msg->name[i], name )) + { + return msg->value[i]; + } + } + return ""; +} +void httpd_MsgAdd( httpd_message_t *msg, char *name, char *psz_value, ... ) +{ + va_list args; + char *value = NULL; + + va_start( args, psz_value ); +#if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN) && !defined(SYS_BEOS) + vasprintf( &value, psz_value, args ); +#else + { + int i_size = strlen( psz_value ) + 4096; /* FIXME stupid system */ + value = calloc( i_size, sizeof( char ) ); + vsnprintf( value, i_size, psz_value, args ); + value[i_size - 1] = 0; + } +#endif + va_end( args ); + + name = strdup( name ); + + TAB_APPEND( msg->i_name, msg->name, name ); + TAB_APPEND( msg->i_value, msg->value, value ); +} + +static void httpd_ClientInit( httpd_client_t *cl ) +{ + cl->i_state = HTTPD_CLIENT_RECEIVING; + cl->i_activity_date = mdate(); + cl->i_activity_timeout = 1500000000LL; + cl->i_buffer_size = 10000; + cl->i_buffer = 0; + cl->p_buffer = malloc( cl->i_buffer_size ); + cl->i_mode = HTTPD_CLIENT_FILE; + cl->b_read_waiting = VLC_FALSE; + + httpd_MsgInit( &cl->query ); + httpd_MsgInit( &cl->answer ); +} +void httpd_ClientModeStream( httpd_client_t *cl ) +{ + cl->i_mode = HTTPD_CLIENT_STREAM; +} +void httpd_ClientModeBidir( httpd_client_t *cl ) +{ + cl->i_mode = HTTPD_CLIENT_BIDIR; +} + +static void httpd_ClientClean( httpd_client_t *cl ) +{ + if( cl->fd > 0 ) + { + SOCKET_CLOSE( cl->fd ); + } + + httpd_MsgClean( &cl->answer ); + httpd_MsgClean( &cl->query ); + + if( cl->p_buffer ) + { + free( cl->p_buffer ); + } +} + +static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_in *sock ) +{ + httpd_client_t *cl = malloc( sizeof( httpd_client_t ) ); + /* set this new socket non-block */ +#if defined( WIN32 ) || defined( UNDER_CE ) + { + unsigned long i_dummy = 1; + ioctlsocket( fd, FIONBIO, &i_dummy ); + } +#else + fcntl( fd, F_SETFL, O_NONBLOCK ); +#endif + cl->i_ref = 0; + cl->fd = fd; + cl->sock = *sock; + cl->url = NULL; + + httpd_ClientInit( cl ); + + return cl; +} + +static void httpd_ClientRecv( httpd_client_t *cl ) +{ + int i_len; + + if( cl->query.i_proto == HTTPD_PROTO_NONE ) + { + /* enought to see if it's rtp over rtsp or RTSP/HTTP */ + i_len = recv( cl->fd, &cl->p_buffer[cl->i_buffer], 4 - cl->i_buffer, 0 ); + + if( i_len > 0 ) + { + cl->i_buffer += i_len; + } + + if( cl->i_buffer >= 4 ) + { + fprintf( stderr, "peek=%4.4s\n", cl->p_buffer ); + /* detect type */ + if( cl->p_buffer[0] == '$' ) + { + /* RTSP (rtp over rtsp) */ + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_type = HTTPD_MSG_CHANNEL; + cl->query.i_channel = cl->p_buffer[1]; + cl->query.i_body = (cl->p_buffer[2] << 8)|cl->p_buffer[3]; + cl->query.p_body = malloc( cl->query.i_body ); + + cl->i_buffer = 0; + } + else if( !strncmp( cl->p_buffer, "HTTP", 4 ) ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + cl->query.i_type = HTTPD_MSG_ANSWER; + } + else if( !strncmp( cl->p_buffer, "RTSP", 4 ) ) + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_type = HTTPD_MSG_ANSWER; + } + else if( !strncmp( cl->p_buffer, "GET", 3 ) || !strncmp( cl->p_buffer, "HEAD", 4 ) || !strncmp( cl->p_buffer, "POST", 4 ) ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + cl->query.i_type = HTTPD_MSG_NONE; + } + else + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_type = HTTPD_MSG_NONE; + } + } + } + else if( cl->query.i_body > 0 ) + { + /* we are reading the body of a request or a channel */ + i_len = recv( cl->fd, &cl->query.p_body[cl->i_buffer], cl->query.i_body - cl->i_buffer, 0 ); + if( i_len > 0 ) + { + cl->i_buffer += i_len; + } + if( cl->i_buffer >= cl->query.i_body ) + { + cl->i_state = HTTPD_CLIENT_RECEIVE_DONE; + } + } + else + { + /* we are reading a header -> char by char */ + for( ;; ) + { + i_len = recv( cl->fd, &cl->p_buffer[cl->i_buffer], 1, 0 ); + if( i_len <= 0 ) + { + break; + } + cl->i_buffer++; + + if( cl->i_buffer + 1 >= cl->i_buffer_size ) + { + cl->i_buffer_size += 1024; + cl->p_buffer = realloc( cl->p_buffer, cl->i_buffer_size ); + } + if( ( cl->i_buffer >= 2 && !strncmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )|| + ( cl->i_buffer >= 4 && !strncmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) ) + { + char *p; + + /* we have finished the header so parse it and set i_body */ + cl->p_buffer[cl->i_buffer] = '\0'; + + if( cl->query.i_type == HTTPD_MSG_ANSWER ) + { + cl->query.i_status = strtol( &cl->p_buffer[strlen( "HTTP/1.x" )], &p, 0 ); + while( *p == ' ' ) + { + p++; + } + cl->query.psz_status = strdup( p ); + } + else + { + static const struct + { + char *name; + int i_type; + int i_proto; + } + msg_type[] = + { + { "GET", HTTPD_MSG_GET, HTTPD_PROTO_HTTP }, + { "HEAD", HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP }, + { "POST", HTTPD_MSG_POST, HTTPD_PROTO_HTTP }, + + { "OPTIONS", HTTPD_MSG_OPTIONS, HTTPD_PROTO_RTSP }, + { "DESCRIBE", HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP }, + { "SETUP", HTTPD_MSG_SETUP, HTTPD_PROTO_RTSP }, + { "PLAY", HTTPD_MSG_PLAY, HTTPD_PROTO_RTSP }, + { "PAUSE", HTTPD_MSG_PAUSE, HTTPD_PROTO_RTSP }, + { "TEARDOWN", HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP }, + + { NULL, HTTPD_MSG_NONE, HTTPD_PROTO_NONE } + }; + int i; + + p = NULL; + cl->query.i_type = HTTPD_MSG_NONE; + + fprintf( stderr, "received new request=%s\n", cl->p_buffer); + + for( i = 0; msg_type[i].name != NULL; i++ ) + { + if( !strncmp( cl->p_buffer, msg_type[i].name, strlen( msg_type[i].name ) ) ) + { + p = &cl->p_buffer[strlen(msg_type[i].name) + 1 ]; + cl->query.i_type = msg_type[i].i_type; + if( cl->query.i_proto != msg_type[i].i_proto ) + { + p = NULL; + cl->query.i_proto = HTTPD_PROTO_NONE; + cl->query.i_type = HTTPD_MSG_NONE; + } + break; + } + } + if( p == NULL ) + { + if( strstr( cl->p_buffer, "HTTP/1." ) ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + } + else if( strstr( cl->p_buffer, "RTSP/1." ) ) + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + } + } + else + { + char *p2; + char *p3; + + while( *p == ' ' ) + { + p++; + } + p2 = strchr( p, ' ' ); + if( p2 ) + { + *p2++ = '\0'; + } + if( !strncasecmp( p, "rtsp:", 5 ) ) + { + /* for rtsp url, you have rtsp://localhost:port/path */ + p += 5; + while( *p == '/' ) p++; + while( *p && *p != '/' ) p++; + } + cl->query.psz_url = strdup( p ); + if( ( p3 = strchr( cl->query.psz_url, '?' ) ) ) + { + *p3++ = '\0'; + cl->query.psz_args = strdup( p3 ); + } + if( p2 ) + { + while( *p2 == ' ' ) + { + p2++; + } + if( !strncasecmp( p2, "HTTP/1.", 7 ) ) + { + cl->query.i_proto = HTTPD_PROTO_HTTP; + cl->query.i_version = atoi( p2+7 ); + } + else if( !strncasecmp( p2, "RTSP/1.", 7 ) ) + { + cl->query.i_proto = HTTPD_PROTO_RTSP; + cl->query.i_version = atoi( p2+7 ); + } + } + p = p2; + } + } + if( p ) + { + p = strchr( p, '\n' ); + } + if( p ) + { + while( *p == '\n' || *p == '\r' ) + { + p++; + } + while( p && *p != '\0' ) + { + char *line = p; + char *eol = p = strchr( p, '\n' ); + char *colon; + + while( eol && eol >= line && ( *eol == '\n' || *eol == '\r' ) ) + { + *eol-- = '\0'; + } + + if( ( colon = strchr( line, ':' ) ) ) + { + char *name; + char *value; + + *colon++ = '\0'; + while( *colon == ' ' ) + { + colon++; + } + name = strdup( line ); + value = strdup( colon ); + + TAB_APPEND( cl->query.i_name, cl->query.name, name ); + TAB_APPEND( cl->query.i_value,cl->query.value,value); + + if( !strcasecmp( name, "Content-Length" ) ) + { + cl->query.i_body = atol( value ); + } + } + + if( p ) + { + p++; + while( *p == '\n' || *p == '\r' ) + { + p++; + } + } + } + } + if( cl->query.i_body > 0 ) + { + /* TODO Mhh, handle the case client will only send a request and close the connection + * to mark and of body (probably only RTSP) */ + cl->query.p_body = malloc( cl->query.i_body ); + cl->i_buffer = 0; + } + else + { + cl->i_state = HTTPD_CLIENT_RECEIVE_DONE; + } + } + } + } + + /* check if the client is to be set to dead */ +#if defined( WIN32 ) || defined( UNDER_CE ) + if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) ) +#else + if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) ) +#endif + { + if( cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE ) + { + /* connection closed -> end of data */ + if( cl->query.i_body > 0 ) + { + cl->query.i_body = cl->i_buffer; + } + cl->i_state = HTTPD_CLIENT_RECEIVE_DONE; + } + else + { + cl->i_state = HTTPD_CLIENT_DEAD; + } + } + cl->i_activity_date = mdate(); + + /* Debugging only */ + if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) + { + int i; + + fprintf( stderr, "received new request\n" ); + fprintf( stderr, " - proto=%s\n", cl->query.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP" ); + fprintf( stderr, " - version=%d\n", cl->query.i_version ); + fprintf( stderr, " - msg=%d\n", cl->query.i_type ); + if( cl->query.i_type == HTTPD_MSG_ANSWER ) + { + fprintf( stderr, " - answer=%d '%s'\n", cl->query.i_status, cl->query.psz_status ); + } + else if( cl->query.i_type != HTTPD_MSG_NONE ) + { + fprintf( stderr, " - url=%s\n", cl->query.psz_url ); + } + for( i = 0; i < cl->query.i_name; i++ ) + { + fprintf( stderr, " - option name='%s' value='%s'\n", cl->query.name[i], cl->query.value[i] ); + } + } +} + +static void httpd_ClientSend( httpd_client_t *cl ) +{ + int i; + int i_len; + + if( cl->i_buffer < 0 ) + { + /* We need to create the header */ + int i_size = 0; + char *p; + + i_size = strlen( "HTTP/1.") + 10 + 10 + + strlen( cl->answer.psz_status ? cl->answer.psz_status : "" ) + 5; + for( i = 0; i < cl->answer.i_name; i++ ) + { + i_size += strlen( cl->answer.name[i] ) + 2 + strlen( cl->answer.value[i] ) + 2; + } + + if( cl->i_buffer_size < i_size ) + { + cl->i_buffer_size = i_size; + free( cl->p_buffer ); + cl->p_buffer = malloc( i_size ); + } + p = cl->p_buffer; + + p += sprintf( p, "%s/1.%d %d %s\r\n", + cl->answer.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP", + cl->answer.i_version, + cl->answer.i_status, cl->answer.psz_status ); + for( i = 0; i < cl->answer.i_name; i++ ) + { + p += sprintf( p, "%s: %s\r\n", cl->answer.name[i], cl->answer.value[i] ); + } + p += sprintf( p, "\r\n" ); + + cl->i_buffer = 0; + cl->i_buffer_size = (uint8_t*)p - cl->p_buffer; + + fprintf( stderr, "sending answer\n" ); + fprintf( stderr, "%s", cl->p_buffer ); + } + + i_len = send( cl->fd, &cl->p_buffer[cl->i_buffer], cl->i_buffer_size - cl->i_buffer, 0 ); + if( i_len > 0 ) + { + cl->i_activity_date = mdate(); + cl->i_buffer += i_len; + + if( cl->i_buffer >= cl->i_buffer_size ) + { + if( cl->answer.i_body == 0 && cl->answer.i_body_offset > 0 && !cl->b_read_waiting ) + { + /* catch more body data */ + int i_msg = cl->query.i_type; + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, + &cl->answer, &cl->query ); + } + + if( cl->answer.i_body > 0 ) + { + /* send the body data */ + free( cl->p_buffer ); + cl->p_buffer = cl->answer.p_body; + cl->i_buffer_size = cl->answer.i_body; + cl->i_buffer = 0; + + cl->answer.i_body = 0; + cl->answer.p_body = NULL; + } + else + { + /* send finished */ + cl->i_state = HTTPD_CLIENT_SEND_DONE; + } + } + } + else + { +#if defined( WIN32 ) || defined( UNDER_CE ) + if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) ) +#else + if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) ) +#endif + { + /* error */ + cl->i_state = HTTPD_CLIENT_DEAD; + } + } +} + +static void httpd_HostThread( httpd_host_t *host ) +{ + while( !host->b_die ) + { + struct timeval timeout; + fd_set fds_read; + fd_set fds_write; + int i_handle_max = 0; + int i_ret; + int i_client; + int b_low_delay = 0; + + if( host->i_url <= 0 ) + { + /* 0.2s */ + msleep( 200000 ); + continue; + } + + /* built a set of handle to select */ + FD_ZERO( &fds_read ); + FD_ZERO( &fds_write ); + + FD_SET( host->fd, &fds_read ); + i_handle_max = host->fd; + + /* add all socket that should be read/write and close dead connection */ + vlc_mutex_lock( &host->lock ); + for( i_client = 0; i_client < host->i_client; i_client++ ) + { + httpd_client_t *cl = host->client[i_client]; + + if( cl->i_ref < 0 || + ( cl->i_ref == 0 && + ( cl->i_state == HTTPD_CLIENT_DEAD || + cl->i_activity_date + cl->i_activity_timeout < mdate() ) ) ) + { + msg_Dbg( host, "connection closed(%s)", inet_ntoa(cl->sock.sin_addr) ); + + httpd_ClientClean( cl ); + + TAB_REMOVE( host->i_client, host->client, cl ); + i_client--; + } + else if( cl->i_state == HTTPD_CLIENT_RECEIVING ) + { + FD_SET( cl->fd, &fds_read ); + i_handle_max = __MAX( i_handle_max, cl->fd ); + } + else if( cl->i_state == HTTPD_CLIENT_SENDING ) + { + FD_SET( cl->fd, &fds_write ); + i_handle_max = __MAX( i_handle_max, cl->fd ); + } + else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE ) + { + httpd_message_t *answer = &cl->answer; + httpd_message_t *query = &cl->query; + int i_msg = query->i_type; + + httpd_MsgInit( answer ); + + /* Handle what we received */ + if( cl->i_mode != HTTPD_CLIENT_BIDIR && ( i_msg == HTTPD_MSG_ANSWER || i_msg == HTTPD_MSG_CHANNEL ) ) + { + /* we can only receive request from client when not in BIDIR mode */ + cl->url = NULL; + cl->i_state = HTTPD_CLIENT_DEAD; + } + else if( i_msg == HTTPD_MSG_ANSWER ) + { + /* We are in BIDIR mode, trigger the callback and then check for new data */ + if( cl->url && cl->url->catch[i_msg].cb ) + { + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, NULL, query ); + } + cl->i_state = HTTPD_CLIENT_WAITING; + } + else if( i_msg == HTTPD_MSG_CHANNEL ) + { + /* We are in BIDIR mode, trigger the callback and then check for new data */ + if( cl->url && cl->url->catch[i_msg].cb ) + { + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, NULL, query ); + } + cl->i_state = HTTPD_CLIENT_WAITING; + } + else if( i_msg == HTTPD_MSG_OPTIONS ) + { + int i_cseq; + + /* unimplemented */ + answer->i_proto = query->i_proto ; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_version= 0; + answer->i_status = 200; + answer->psz_status = strdup( "Ok" ); + + answer->i_body = 0; + answer->p_body = NULL; + + i_cseq = atoi( httpd_MsgGet( query, "Cseq" ) ); + httpd_MsgAdd( answer, "Cseq", "%d", i_cseq ); + httpd_MsgAdd( answer, "Server", "VLC Server" ); + httpd_MsgAdd( answer, "Public", "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE" ); + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ + cl->i_state = HTTPD_CLIENT_SENDING; + } + else if( i_msg == HTTPD_MSG_NONE ) + { + if( query->i_proto == HTTPD_PROTO_NONE ) + { + cl->url = NULL; + cl->i_state = HTTPD_CLIENT_DEAD; + } + else + { + uint8_t *p; + + /* unimplemented */ + answer->i_proto = query->i_proto ; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_version= 0; + answer->i_status = 501; + answer->psz_status = strdup( "Unimplemented" ); + + p = answer->p_body = malloc( 1000 ); + + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "Error 501\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "

501 Unimplemented

\n" ); + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + + answer->i_body = p - answer->p_body; + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + + cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ + cl->i_state = HTTPD_CLIENT_SENDING; + } + } + else + { + vlc_bool_t b_auth_failed = VLC_FALSE; + int i; + + /* Search the url and trigger callbacks */ + for( i = 0; i < host->i_url; i++ ) + { + httpd_url_t *url = host->url[i]; + + if( !strcmp( url->psz_url, query->psz_url ) ) + { + if( url->catch[i_msg].cb ) + { + if( answer && ( *url->psz_user || *url->psz_password ) ) + { + /* create the headers */ + char id[strlen(url->psz_user)+strlen(url->psz_password) + 2]; + char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */ + char auth[strlen(b64) +1]; + + sprintf( id, "%s:%s", url->psz_user, url->psz_password ); + if( !strncasecmp( b64, "BASIC", 5 ) ) + { + b64 += 5; + while( *b64 == ' ' ) + { + b64++; + } + b64_decode( auth, b64 ); + } + else + { + strcpy( auth, "" ); + } + if( strcmp( id, auth ) ) + { + httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user ); + /* We fail for all url */ + b_auth_failed = VLC_TRUE; + break; + } + } + + if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) ) + { + /* only one url can answer */ + answer = NULL; + if( cl->url == NULL ) + { + cl->url = url; + } + } + } + } + } + if( answer ) + { + uint8_t *p; + + answer->i_proto = query->i_proto; + answer->i_type = HTTPD_MSG_ANSWER; + answer->i_version= 0; + p = answer->p_body = malloc( 1000 + strlen(query->psz_url) ); + + if( b_auth_failed ) + { + answer->i_status = 401; + answer->psz_status = strdup( "Authorization Required" ); + + 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 Authorization Required (%s)

\n", query->psz_url ); + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + } + else + { + /* no url registered */ + answer->i_status = 404; + answer->psz_status = strdup( "Not found" ); + + 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(%s)

\n", query->psz_url ); + p += sprintf( p, "
\n" ); + p += sprintf( p, "VideoLAN\n" ); + p += sprintf( p, "\n" ); + p += sprintf( p, "\n" ); + } + + answer->i_body = p - answer->p_body; + httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body ); + } + cl->i_buffer = -1; /* Force the creation of the answer in httpd_ClientSend */ + cl->i_state = HTTPD_CLIENT_SENDING; + } + } + else if( cl->i_state == HTTPD_CLIENT_SEND_DONE ) + { + if( cl->i_mode == HTTPD_CLIENT_FILE ) + { + cl->url = NULL; + if( ( cl->query.i_proto == HTTPD_PROTO_HTTP && + ( !strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Keep-Alive" )|| + ( cl->answer.i_version == 1 && strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Close" ) ) ) ) || + ( cl->query.i_proto == HTTPD_PROTO_RTSP && + strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Close" ) && + strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Close" ) ) ) + { + httpd_MsgClean( &cl->query ); + httpd_MsgInit( &cl->query ); + + cl->i_buffer = 0; + cl->i_buffer_size = 1000; + free( cl->p_buffer ); + cl->p_buffer = malloc( cl->i_buffer_size ); + cl->i_state = HTTPD_CLIENT_RECEIVING; + } + else + { + cl->i_state = HTTPD_CLIENT_DEAD; + } + httpd_MsgClean( &cl->answer ); + } + else if( cl->b_read_waiting ) + { + /* we have a message waiting for us to read it */ + httpd_MsgClean( &cl->answer ); + httpd_MsgClean( &cl->query ); + + cl->i_buffer = 0; + cl->i_buffer_size = 1000; + free( cl->p_buffer ); + cl->p_buffer = malloc( cl->i_buffer_size ); + cl->i_state = HTTPD_CLIENT_RECEIVING; + cl->b_read_waiting = VLC_FALSE; + } + else + { + int64_t i_offset = cl->answer.i_body_offset; + httpd_MsgClean( &cl->answer ); + + cl->answer.i_body_offset = i_offset; + cl->i_state = HTTPD_CLIENT_WAITING; + } + } + else if( cl->i_state == HTTPD_CLIENT_WAITING ) + { + int64_t i_offset = cl->answer.i_body_offset; + int i_msg = cl->query.i_type; + + httpd_MsgInit( &cl->answer ); + cl->answer.i_body_offset = i_offset; + + cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, &cl->answer, &cl->query ); + if( cl->answer.i_type != HTTPD_MSG_NONE ) + { + /* we have new data, so reenter send mode */ + cl->i_buffer = 0; + cl->p_buffer = cl->answer.p_body; + cl->i_buffer_size = cl->answer.i_body; + cl->answer.p_body = NULL; + cl->answer.i_body = 0; + cl->i_state = HTTPD_CLIENT_SENDING; + } + else + { + /* we shouldn't wait too long */ + b_low_delay = VLC_TRUE; + } + } + + /* Special for BIDIR mode we also check reading */ + if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING ) + { + FD_SET( cl->fd, &fds_read ); + i_handle_max = __MAX( i_handle_max, cl->fd ); + } + } + vlc_mutex_unlock( &host->lock ); + + /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */ + timeout.tv_sec = 0; + timeout.tv_usec = b_low_delay ? 20000 : 100000; + + i_ret = select( i_handle_max + 1, + &fds_read, &fds_write, NULL, &timeout ); + + if( i_ret == -1 && errno != EINTR ) + { + msg_Warn( host, "cannot select sockets" ); + msleep( 1000 ); + continue; + } + else if( i_ret <= 0 ) + { + continue; + } + + /* accept new connections */ + if( FD_ISSET( host->fd, &fds_read ) ) + { + int i_sock_size = sizeof( struct sockaddr_in ); + struct sockaddr_in sock; + int fd; + + fd = accept( host->fd, (struct sockaddr *)&sock, &i_sock_size ); + if( fd > 0 ) + { + httpd_client_t *cl = httpd_ClientNew( fd, &sock ); + + vlc_mutex_lock( &host->lock ); + TAB_APPEND( host->i_client, host->client, cl ); + vlc_mutex_unlock( &host->lock ); + + msg_Dbg( host, "new connection (%s)", inet_ntoa(sock.sin_addr) ); + } + } + /* now try all others socket */ + vlc_mutex_lock( &host->lock ); + for( i_client = 0; i_client < host->i_client; i_client++ ) + { + httpd_client_t *cl = host->client[i_client]; + if( cl->i_state == HTTPD_CLIENT_RECEIVING ) + { + httpd_ClientRecv( cl ); + } + else if( cl->i_state == HTTPD_CLIENT_SENDING ) + { + httpd_ClientSend( cl ); + } + + if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING && + FD_ISSET( cl->fd, &fds_read ) ) + { + cl->b_read_waiting = VLC_TRUE; + } + } + vlc_mutex_unlock( &host->lock ); + } +} + + + + + +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 ); +} -- 2.39.2