Commit 2d4c56cd authored by Laurent Aimar's avatar Laurent Aimar

* include/httpd.h, modules/misc/httpd.c: remove old http daemon.

 * include/vlc_httpd.h src/misc/httpd.c: added new http daemon,
 it will allow http 1.1, redirection, RTSP, ...
parent 43cf73c3
/*****************************************************************************
* 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 );
}
}
/*****************************************************************************
* 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 */
/*****************************************************************************
* 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 );
}
/*****************************************************************************
* 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 );
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment