+++ /dev/null
-/*****************************************************************************
- * httpd.h
- *****************************************************************************
- * Copyright (C) 2001-2003 VideoLAN
- * $Id: httpd.h,v 1.7 2003/07/11 09:50:10 gbazin Exp $
- *
- * Authors: Laurent Aimar <fenrir@via.ecp.fr>
- *
- * 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 );
- }
-}
--- /dev/null
+/*****************************************************************************
+ * 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 <fenrir@via.ecp.fr>
+ *
+ * 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 */
+++ /dev/null
-/*****************************************************************************
- * httpd.c
- *****************************************************************************
- * Copyright (C) 2001-2003 VideoLAN
- * $Id: httpd.c,v 1.31 2004/02/05 19:51:46 fenrir Exp $
- *
- * Authors: Laurent Aimar <fenrir@via.ecp.fr>
- *
- * 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 <stdlib.h>
-#include <vlc/vlc.h>
-
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-
-#include "httpd.h"
-
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-#if defined( UNDER_CE )
-# include <winsock.h>
-#elif defined( WIN32 )
-# include <winsock2.h>
-# include <ws2tcpip.h>
-# ifndef IN_MULTICAST
-# define IN_MULTICAST(a) IN_CLASSD(a)
-# endif
-#else
-# include <netdb.h> /* hostent ... */
-# include <sys/socket.h>
-# include <netinet/in.h>
-# ifdef HAVE_ARPA_INET_H
-# include <arpa/inet.h> /* 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, "<html>\n" );
- p += sprintf( p, "<head>\n" );
- p += sprintf( p, "<title>Error 400</title>\n" );
- p += sprintf( p, "</head>\n" );
- p += sprintf( p, "<body>\n" );
- p += sprintf( p, "<h1><center> 400 Bad Request</center></h1>\n" );
- p += sprintf( p, "<hr />\n" );
- p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
- p += sprintf( p, "</body>\n" );
- p += sprintf( p, "</html>\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, "<html>\n" );
- p += sprintf( p, "<head>\n" );
- p += sprintf( p, "<title>Error 401</title>\n" );
- p += sprintf( p, "</head>\n" );
- p += sprintf( p, "<body>\n" );
- p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
- p += sprintf( p, "<hr />\n" );
- p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
- p += sprintf( p, "</body>\n" );
- p += sprintf( p, "</html>\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, "<html>\n" );
- p += sprintf( p, "<head>\n" );
- p += sprintf( p, "<title>Error 404</title>\n" );
- p += sprintf( p, "</head>\n" );
- p += sprintf( p, "<body>\n" );
- p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
- p += sprintf( p, "<hr />\n" );
- p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
- p += sprintf( p, "</body>\n" );
- p += sprintf( p, "</html>\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 );
-}
--- /dev/null
+/*****************************************************************************
+ * httpd.c
+ *****************************************************************************
+ * Copyright (C) 2004 VideoLAN
+ * $Id: httpd.c,v 1.1 2004/03/03 13:23:47 fenrir Exp $
+ *
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
+ *
+ * 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 <stdlib.h>
+#include <vlc/vlc.h>
+
+#include "vlc_httpd.h"
+
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#if defined( UNDER_CE )
+# include <winsock.h>
+#elif defined( WIN32 )
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# ifndef IN_MULTICAST
+# define IN_MULTICAST(a) IN_CLASSD(a)
+# endif
+#else
+# include <netdb.h> /* hostent ... */
+# include <sys/socket.h>
+# include <netinet/in.h>
+# ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h> /* 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, "<html>\n" );
+ p += sprintf( p, "<head>\n" );
+ p += sprintf( p, "<title>Redirection</title>\n" );
+ p += sprintf( p, "</head>\n" );
+ p += sprintf( p, "<body>\n" );
+ p += sprintf( p, "<h1><center>You should be <a href=\"%s\">redirected</a></center></h1>\n", rdir->psz_dst );
+ p += sprintf( p, "<hr />\n" );
+ p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
+ p += sprintf( p, "</body>\n" );
+ p += sprintf( p, "</html>\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, "<html>\n" );
+ p += sprintf( p, "<head>\n" );
+ p += sprintf( p, "<title>Error 501</title>\n" );
+ p += sprintf( p, "</head>\n" );
+ p += sprintf( p, "<body>\n" );
+ p += sprintf( p, "<h1><center> 501 Unimplemented</center></h1>\n" );
+ p += sprintf( p, "<hr />\n" );
+ p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
+ p += sprintf( p, "</body>\n" );
+ p += sprintf( p, "</html>\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, "<html>\n" );
+ p += sprintf( p, "<head>\n" );
+ p += sprintf( p, "<title>Error 401</title>\n" );
+ p += sprintf( p, "</head>\n" );
+ p += sprintf( p, "<body>\n" );
+ p += sprintf( p, "<h1><center> 401 Authorization Required (%s)</center></h1>\n", query->psz_url );
+ p += sprintf( p, "<hr />\n" );
+ p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
+ p += sprintf( p, "</body>\n" );
+ p += sprintf( p, "</html>\n" );
+ }
+ else
+ {
+ /* no url registered */
+ answer->i_status = 404;
+ answer->psz_status = strdup( "Not found" );
+
+ p += sprintf( p, "<html>\n" );
+ p += sprintf( p, "<head>\n" );
+ p += sprintf( p, "<title>Error 404</title>\n" );
+ p += sprintf( p, "</head>\n" );
+ p += sprintf( p, "<body>\n" );
+ p += sprintf( p, "<h1><center> 404 Ressource not found(%s)</center></h1>\n", query->psz_url );
+ p += sprintf( p, "<hr />\n" );
+ p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
+ p += sprintf( p, "</body>\n" );
+ p += sprintf( p, "</html>\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 );
+}