Commit 84aaa859 authored by Laurent Aimar's avatar Laurent Aimar

* modules/access_output/http : http output.

 * httpd : mini http server (be carefull about security issue...)
parent 4d12c66c
/*****************************************************************************
* httpd.h
*****************************************************************************
* Copyright (C) 2001-2003 VideoLAN
* $Id: httpd.h,v 1.1 2003/02/23 19:05:22 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.
*****************************************************************************/
typedef struct httpd_t httpd_t;
typedef struct httpd_host_t httpd_host_t;
typedef struct httpd_file_t httpd_file_t;
//typedef struct httpd_stream_t httpd_stream_t;
typedef httpd_file_t httpd_stream_t;
typedef struct httpd_file_callback_args_t httpd_file_callback_args_t;
typedef int (*httpd_file_callback)( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data );
typedef struct httpd_sys_t httpd_sys_t;
struct httpd_t
{
VLC_COMMON_MEMBERS
module_t *p_module;
httpd_sys_t *p_sys;
httpd_host_t *(*pf_register_host) ( httpd_t *, char *, int );
void (*pf_unregister_host) ( httpd_t *, httpd_host_t * );
httpd_file_t *(*pf_register_file) ( httpd_t *,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password,
httpd_file_callback pf_fill,
httpd_file_callback_args_t *p_args );
void (*pf_unregister_file) ( httpd_t *, httpd_file_t * );
httpd_stream_t *(*pf_register_stream) ( httpd_t *,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password );
int (*pf_send_stream) ( httpd_t *,
httpd_stream_t *,
uint8_t *, int );
void (*pf_unregister_stream) ( httpd_t *, httpd_stream_t * );
};
/*
* httpd_Find:
*
* Return the running httpd instance
* (if none and b_create then a new one is created)
*/
static inline httpd_t* httpd_Find( vlc_object_t *p_this, vlc_bool_t b_create )
{
httpd_t *p_httpd = NULL;
p_httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE );
if( !p_httpd )
{
if( !b_create )
{
return( NULL );
}
p_httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD );
if( !p_httpd )
{
msg_Err( p_this, "out of memory" );
return( NULL );
}
p_httpd->p_module = module_Need( p_httpd, "httpd", "" );
if( !p_httpd->p_module )
{
msg_Err( p_this, "no suitable httpd module" );
vlc_object_destroy( p_httpd );
return( NULL );
}
vlc_object_yield( p_httpd );
}
return( p_httpd );
}
static inline void httpd_Release( httpd_t *p_httpd )
{
vlc_object_release( p_httpd );
}
/*****************************************************************************
* http.c
*****************************************************************************
* Copyright (C) 2001-2003 VideoLAN
* $Id: http.c,v 1.1 2003/02/23 19:05:22 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
*****************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <vlc/vlc.h>
#include <vlc/input.h>
#include <vlc/sout.h>
#include "httpd.h"
#define FREE( p ) if( p ) { free( p); (p) = NULL; }
#define DEFAULT_PORT 8080
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
static int Write( sout_access_out_t *, sout_buffer_t * );
static int Seek ( sout_access_out_t *, off_t );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("HTTP stream ouput") );
set_capability( "sout access", 0 );
add_shortcut( "http" );
set_callbacks( Open, Close );
vlc_module_end();
struct sout_access_out_sys_t
{
httpd_t *p_httpd;
/* host */
httpd_host_t *p_httpd_host;
/* stream */
httpd_stream_t *p_httpd_stream;
};
/*****************************************************************************
* Open: open the file
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
sout_access_out_t *p_access = (sout_access_out_t*)p_this;
sout_access_out_sys_t *p_sys;
char *psz_parser, *psz_name;
char *psz_bind_addr;
int i_bind_port;
char *psz_file_name;
if( !( p_sys = p_access->p_sys =
malloc( sizeof( sout_access_out_sys_t ) ) ) )
{
msg_Err( p_access, "Not enough memory" );
return( VLC_EGENERIC );
}
/* *** parse p_access->psz_name to extract bind address, port and file name *** */
/* p_access->psz_name host.name:port/filename */
psz_name = psz_parser = strdup( p_access->psz_name );
psz_bind_addr = psz_parser;
i_bind_port = 0;
psz_file_name = "";
while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
{
psz_parser++;
}
if( *psz_parser == ':' )
{
*psz_parser = '\0';
psz_parser++;
i_bind_port = atoi( psz_parser );
while( *psz_parser && *psz_parser != '/' )
{
psz_parser++;
}
}
if( *psz_parser == '/' )
{
*psz_parser = '\0';
psz_parser++;
psz_file_name = psz_parser;
}
if( i_bind_port <= 0 )
{
i_bind_port = DEFAULT_PORT;
}
if( !*psz_file_name )
{
psz_file_name = strdup( "/" );
}
else if( *psz_file_name != '/' )
{
char *p = psz_file_name;
psz_file_name = malloc( strlen( p ) + 2 );
strcpy( psz_file_name, "/" );
strcat( psz_file_name, p );
}
p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_access), VLC_TRUE );
if( !p_sys->p_httpd )
{
msg_Err( p_access, "cannot start httpd daemon" );
free( psz_name );
free( psz_file_name );
free( p_access );
return( VLC_EGENERIC );
}
p_sys->p_httpd_host =
p_sys->p_httpd->pf_register_host( p_sys->p_httpd,
psz_bind_addr, i_bind_port );
if( !p_sys->p_httpd_host )
{
msg_Err( p_access, "cannot listen on %s:%d", psz_bind_addr, i_bind_port );
httpd_Release( p_sys->p_httpd );
free( psz_name );
free( psz_file_name );
free( p_access );
return( VLC_EGENERIC );
}
p_sys->p_httpd_stream =
p_sys->p_httpd->pf_register_stream( p_sys->p_httpd,
psz_file_name, "application/x-octet_stream",
NULL, NULL );
if( !p_sys->p_httpd_stream )
{
msg_Err( p_access, "cannot add stream %s", psz_file_name );
p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd, p_sys->p_httpd_host );
httpd_Release( p_sys->p_httpd );
free( psz_name );
free( psz_file_name );
free( p_access );
return( VLC_EGENERIC );
}
p_access->pf_write = Write;
p_access->pf_seek = Seek;
return VLC_SUCCESS;
}
/*****************************************************************************
* Close: close the target
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
sout_access_out_t *p_access = (sout_access_out_t*)p_this;
sout_access_out_sys_t *p_sys = p_access->p_sys;
p_sys->p_httpd->pf_unregister_stream( p_sys->p_httpd, p_sys->p_httpd_stream );
p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd, p_sys->p_httpd_host );
httpd_Release( p_sys->p_httpd );
msg_Info( p_access, "Close" );
free( p_sys );
}
/*****************************************************************************
* Write:
*****************************************************************************/
static int Write( sout_access_out_t *p_access, sout_buffer_t *p_buffer )
{
sout_access_out_sys_t *p_sys = p_access->p_sys;
int i_err = 0;
while( p_buffer )
{
sout_buffer_t *p_next;
i_err = p_sys->p_httpd->pf_send_stream( p_sys->p_httpd, p_sys->p_httpd_stream,
p_buffer->p_buffer, p_buffer->i_size );
p_next = p_buffer->p_next;
sout_BufferDelete( p_access->p_sout, p_buffer );
p_buffer = p_next;
if( i_err < 0 )
{
break;
}
}
if( i_err < 0 )
{
sout_buffer_t *p_next;
while( p_buffer )
{
p_next = p_buffer->p_next;
sout_BufferDelete( p_access->p_sout, p_buffer );
p_buffer = p_next;
}
}
return( i_err < 0 ? VLC_EGENERIC : VLC_SUCCESS );
}
/*****************************************************************************
* Seek: seek to a specific location in a file
*****************************************************************************/
static int Seek( sout_access_out_t *p_access, off_t i_pos )
{
msg_Err( p_access, "http sout access cannot seek" );
return( VLC_EGENERIC );
}
/*****************************************************************************
* httpd.c
*****************************************************************************
* Copyright (C) 2001-2003 VideoLAN
* $Id: httpd.c,v 1.1 2003/02/23 19:05:22 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
*****************************************************************************/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include "httpd.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
# include <io.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 1024
#define FREE( p ) if( p ) { free( p); (p) = NULL; }
#if defined( WIN32 ) || defined( UNDER_CE )
#define SOCKET_CLOSE closesocket;
#else
#define SOCKET_CLOSE close
#endif
/*****************************************************************************
* Exported prototypes
*****************************************************************************/
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
vlc_module_begin();
set_description( _("HTTP 1.0 daemon") );
set_capability( "httpd", 42 );
set_callbacks( Open, Close );
vlc_module_end();
/*****************************************************************************
* Prototypes
*****************************************************************************/
static httpd_host_t *RegisterHost( httpd_t *, char *, int );
static void UnregisterHost( httpd_t *, httpd_host_t * );
static httpd_file_t *RegisterFile( httpd_t *,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password,
httpd_file_callback pf_fill,
httpd_file_callback_args_t *p_args );
static void UnregisterFile( httpd_t *, httpd_file_t * );
//#define httpd_stream_t httpd_file_t
static httpd_stream_t *RegisterStream( httpd_t *,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password );
static int SendStream( httpd_t *, httpd_stream_t *, uint8_t *, int );
static void UnregisterStream( httpd_t *, httpd_stream_t* );
/*****************************************************************************
* Internal definitions
*****************************************************************************/
struct httpd_host_t
{
int i_ref;
char *psz_host_addr;
int i_port;
struct sockaddr_in sock;
int fd;
};
#define HTTPD_AUTHENTICATE_NONE 0
#define HTTPD_AUTHENTICATE_BASIC 1
//typedef httpd_file_t httpd_stream_t;
struct httpd_file_t
{
int i_ref;
char *psz_file;
char *psz_mime;
int i_authenticate_method;
char *psz_user; /* NULL if no auth */
char *psz_password; /* NULL if no auth */
vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback
true: it's up to the program to give data to httpd */
void *p_sys; /* provided for user */
httpd_file_callback pf_fill; /* it should allocate and fill *pp_data and *pi_data */
/* private */
int i_buffer_size; /* buffer size */
uint8_t *p_buffer; /* buffer */
int i_buffer; /* reading pointer */
int i_buffer_valid; /* valid data from 0 */
};
#define HTTPD_CONNECTION_RECEIVING_REQUEST 1
#define HTTPD_CONNECTION_SENDING_HEADER 2
#define HTTPD_CONNECTION_SENDING_FILE 3
#define HTTPD_CONNECTION_SENDING_STREAM 4
typedef struct httpd_connection_s
{
struct httpd_connection_s *p_next;
struct httpd_connection_s *p_prev;
struct sockaddr_in sock;
int fd;
int i_state;
char *psz_file; // file to be send
int i_http_error; // error to be send with the file
char *psz_user; // if Authorization in the request header
char *psz_password;
httpd_file_t *p_file;
int i_buffer_size;
uint8_t *p_buffer;
int i_buffer; /* private */
} httpd_connection_t;
/*
* The httpd thread
*/
struct httpd_sys_t
{
VLC_COMMON_MEMBERS
vlc_mutex_t host_lock;
volatile int i_host_count;
httpd_host_t **host;
vlc_mutex_t file_lock;
int i_file_count;
httpd_file_t **file;
vlc_mutex_t connection_lock;
int i_connection_count;
httpd_connection_t *p_first_connection;
};
static void httpd_Thread( httpd_sys_t *p_httpt );
static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
/*****************************************************************************
* Open:
*****************************************************************************/
static int Open( vlc_object_t *p_this )
{
httpd_t *p_httpd = (httpd_t*)p_this;
httpd_sys_t *p_httpt;
/* Launch httpt thread */
if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
{
msg_Err( p_this, "out of memory" );
return( VLC_EGENERIC );
}
p_httpt->b_die = 0;
p_httpt->b_error= 0;
/* init httpt_t structure */
vlc_mutex_init( p_httpd, &p_httpt->host_lock );
p_httpt->i_host_count = 0;
p_httpt->host = NULL;
vlc_mutex_init( p_httpd, &p_httpt->file_lock );
p_httpt->i_file_count = 0;
p_httpt->file = NULL;
vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
p_httpt->i_connection_count = 0;
p_httpt->p_first_connection = NULL;
/* start the thread */
if( vlc_thread_create( p_httpt, "httpd thread",
httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
{
msg_Err( p_this, "cannot spawn http thread" );
vlc_mutex_destroy( &p_httpt->host_lock );
vlc_mutex_destroy( &p_httpt->file_lock );
vlc_mutex_destroy( &p_httpt->connection_lock );
vlc_object_destroy( p_httpt );
return( VLC_EGENERIC );
}
msg_Info( p_httpd, "http thread launched" );
p_httpd->p_sys = p_httpt;
p_httpd->pf_register_host = RegisterHost;
p_httpd->pf_unregister_host = UnregisterHost;
p_httpd->pf_register_file = RegisterFile;
p_httpd->pf_unregister_file = UnregisterFile;
p_httpd->pf_register_stream = RegisterStream;
p_httpd->pf_send_stream = SendStream;
p_httpd->pf_unregister_stream=UnregisterStream;
return( VLC_SUCCESS );
}
/*****************************************************************************
* Close: close the target
*****************************************************************************/
static void Close( vlc_object_t * p_this )
{
httpd_t *p_httpd = (httpd_t*)p_this;
httpd_sys_t *p_httpt = p_httpd->p_sys;
httpd_connection_t *p_con;
int i;
p_httpt->b_die = 1;
vlc_thread_join( p_httpt );
/* first close all host */
vlc_mutex_destroy( &p_httpt->host_lock );
if( p_httpt->i_host_count )
{
msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
}
for( i = 0; i < p_httpt->i_host_count; i++ )
{
#define p_host p_httpt->host[i]
FREE( p_host->psz_host_addr );
SOCKET_CLOSE( p_host->fd );
FREE( p_host );
#undef p_host
}
FREE( p_httpt->host );
/* now all file */
vlc_mutex_destroy( &p_httpt->file_lock );
if( p_httpt->i_file_count )
{
msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
}
for( i = 0; i < p_httpt->i_file_count; i++ )
{
#define p_file p_httpt->file[i]
FREE( p_file->psz_file );
FREE( p_file->psz_mime );
if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
{
FREE( p_file->psz_user );
FREE( p_file->psz_password );
}
FREE( p_file->p_buffer );
FREE( p_file );
#undef p_file
}
FREE( p_httpt->file );
/* andd close all connection */
vlc_mutex_destroy( &p_httpt->connection_lock );
if( p_httpt->i_connection_count )
{
msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
}
while( ( p_con = p_httpt->p_first_connection ) )
{
httpd_ConnnectionClose( p_httpt, p_con );
}
msg_Info( p_httpd, "httpd instance closed" );
vlc_object_destroy( p_httpt );
}
/****************************************************************************
****************************************************************************
***
***
****************************************************************************
****************************************************************************/
static int BuildAddr( struct sockaddr_in * p_socket,
const char * psz_address, int i_port )
{
/* Reset struct */
memset( p_socket, 0, sizeof( struct sockaddr_in ) );
p_socket->sin_family = AF_INET; /* family */
p_socket->sin_port = htons( (uint16_t)i_port );
if( !*psz_address )
{
p_socket->sin_addr.s_addr = INADDR_ANY;
}
else
{
struct hostent * p_hostent;
/* Try to convert address directly from in_addr - this will work if
* psz_address is dotted decimal. */
#ifdef HAVE_ARPA_INET_H
if( !inet_aton( psz_address, &p_socket->sin_addr ) )
#else
p_socket->sin_addr.s_addr = inet_addr( psz_address );
if( p_socket->sin_addr.s_addr == INADDR_NONE )
#endif
{
/* We have a fqdn, try to find its address */
if ( (p_hostent = gethostbyname( psz_address )) == NULL )
{
return( -1 );
}
/* Copy the first address of the host in the socket address */
memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
p_hostent->h_length );
}
}
return( 0 );
}
/*
* listen on a host for a httpd instance
*/
static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
{
httpd_host_t *p_host;
struct sockaddr_in sock;
int i;
int fd = -1;
int i_opt;
int i_flags;
if( BuildAddr( &sock, psz_host_addr, i_port ) )
{
msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
return NULL;
}
/* is it already declared ? */
vlc_mutex_lock( &p_httpt->host_lock );
for( i = 0; i < p_httpt->i_host_count; i++ )
{
if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr )
{
break;
}
}
if( i < p_httpt->i_host_count )
{
/* yes, increment ref count and succed */
p_httpt->host[i]->i_ref++;
vlc_mutex_unlock( &p_httpt->host_lock );
return NULL;
}
/* need to add a new listening socket */
/* open socket */
fd = socket( AF_INET, SOCK_STREAM, 0 );
if( fd < 0 )
{
msg_Err( p_httpt, "cannot open socket" );
goto socket_failed;
}
/* reuse socket */
i_opt = 1;
if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
(void *) &i_opt, sizeof( i_opt ) ) < 0 )
{
msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
}
/* bind it */
if( bind( fd, &sock, sizeof( struct sockaddr_in ) ) < 0 )
{
msg_Err( p_httpt, "cannot bind socket" );
goto socket_failed;
}
/* set to non-blocking */
if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
{
msg_Err( p_httpt, "cannot F_GETFL socket" );
goto socket_failed;
}
if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
{
msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
goto socket_failed;
}
/* listen */
if( listen( fd, LISTEN_BACKLOG ) < 0 )
{
msg_Err( p_httpt, "cannot listen socket" );
goto socket_failed;
}
if( p_httpt->host )
{
p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) );
}
else
{
p_httpt->host = malloc( sizeof( httpd_host_t *) );
}
p_host = malloc( sizeof( httpd_host_t ) );
p_host->i_ref = 1;
p_host->psz_host_addr = strdup( psz_host_addr );
p_host->i_port = i_port;
p_host->sock = sock;
p_host->fd = fd;
p_httpt->host[p_httpt->i_host_count++] = p_host;
vlc_mutex_unlock( &p_httpt->host_lock );
return p_host;
socket_failed:
vlc_mutex_unlock( &p_httpt->host_lock );
if( fd >= 0 )
{
SOCKET_CLOSE( fd );
}
return NULL;
}
static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
{
return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
}
/*
* remove a listening host for an httpd instance
*/
static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
{
int i;
vlc_mutex_lock( &p_httpt->host_lock );
for( i = 0; i < p_httpt->i_host_count; i++ )
{
if( p_httpt->host[i] == p_host )
{
break;
}
}
if( i >= p_httpt->i_host_count )
{
vlc_mutex_unlock( &p_httpt->host_lock );
msg_Err( p_httpt, "cannot unregister host" );
return;
}
p_host->i_ref--;
if( p_host->i_ref > 0 )
{
/* still in use */
vlc_mutex_unlock( &p_httpt->host_lock );
return;
}
/* no more used */
FREE( p_host->psz_host_addr );
SOCKET_CLOSE( p_host->fd );
FREE( p_host );
if( p_httpt->i_host_count <= 1 )
{
FREE( p_httpt->host );
p_httpt->i_host_count = 0;
}
else
{
int i_move;
i_move = p_httpt->i_host_count - i - 1;
if( i_move > 0 )
{
memmove( &p_httpt->host[i],
&p_httpt->host[i+1],
i_move * sizeof( httpd_host_t * ) );
}
p_httpt->i_host_count--;
p_httpt->host = realloc( p_httpt->host,
p_httpt->i_host_count * sizeof( httpd_host_t * ) );
}
vlc_mutex_unlock( &p_httpt->host_lock );
}
static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
{
_UnregisterHost( p_httpd->p_sys, p_host );
}
static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
{
/* add a new file */
if( p_httpt->i_file_count )
{
p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) );
}
else
{
p_httpt->file = malloc( sizeof( httpd_file_t *) );
}
p_httpt->file[p_httpt->i_file_count++] = p_file;
}
static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password,
httpd_file_callback pf_fill,
httpd_file_callback_args_t *p_args )
{
httpd_file_t *p_file;
int i;
vlc_mutex_lock( &p_httpt->file_lock );
for( i = 0; i < p_httpt->i_file_count; i++ )
{
if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
{
break;
}
}
if( i < p_httpt->i_file_count )
{
vlc_mutex_unlock( &p_httpt->file_lock );
msg_Err( p_httpt, "%s already registered", psz_file );
return NULL;
}
p_file = malloc( sizeof( httpd_file_t ) );
p_file->i_ref = 0;
p_file->psz_file = strdup( psz_file );
p_file->psz_mime = strdup( psz_mime );
if( psz_user && *psz_user )
{
p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
p_file->psz_user = strdup( psz_user );
p_file->psz_password = strdup( psz_password );
}
else
{
p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
p_file->psz_user = NULL;
p_file->psz_password = NULL;
}
p_file->b_stream = VLC_FALSE;
p_file->p_sys = p_args;
p_file->pf_fill = pf_fill;
p_file->i_buffer_size = 0;
p_file->i_buffer_valid = 0;
p_file->i_buffer = 0;
p_file->p_buffer = NULL;
__RegisterFile( p_httpt, p_file );
vlc_mutex_unlock( &p_httpt->file_lock );
return p_file;
}
static httpd_file_t *RegisterFile( httpd_t *p_httpd,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password,
httpd_file_callback pf_fill,
httpd_file_callback_args_t *p_args )
{
return( _RegisterFile( p_httpd->p_sys,
psz_file, psz_mime, psz_user, psz_password,
pf_fill, p_args ) );
}
static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password )
{
httpd_stream_t *p_stream;
int i;
vlc_mutex_lock( &p_httpt->file_lock );
for( i = 0; i < p_httpt->i_file_count; i++ )
{
if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
{
break;
}
}
if( i < p_httpt->i_file_count )
{
vlc_mutex_unlock( &p_httpt->file_lock );
msg_Err( p_httpt, "%s already registeret", psz_file );
return NULL;
}
p_stream = malloc( sizeof( httpd_stream_t ) );
p_stream->i_ref = 0;
p_stream->psz_file = strdup( psz_file );
p_stream->psz_mime = strdup( psz_mime );
if( psz_user && *psz_user )
{
p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
p_stream->psz_user = strdup( psz_user );
p_stream->psz_password = strdup( psz_password );
}
else
{
p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
p_stream->psz_user = NULL;
p_stream->psz_password = NULL;
}
p_stream->b_stream = VLC_TRUE;
p_stream->p_sys = NULL;
p_stream->pf_fill = NULL;
p_stream->i_buffer_size = 1024*1024;
p_stream->i_buffer_valid = 0;
p_stream->i_buffer = 0;
p_stream->p_buffer = malloc( p_stream->i_buffer_size );
__RegisterFile( p_httpt, p_stream );
vlc_mutex_unlock( &p_httpt->file_lock );
return p_stream;
}
static httpd_stream_t *RegisterStream( httpd_t *p_httpd,
char *psz_file, char *psz_mime,
char *psz_user, char *psz_password )
{
return( _RegisterStream( p_httpd->p_sys,
psz_file, psz_mime, psz_user, psz_password ) );
}
static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
{
int i;
vlc_mutex_lock( &p_httpt->file_lock );
for( i = 0; i < p_httpt->i_file_count; i++ )
{
if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
{
break;
}
}
if( i >= p_httpt->i_file_count )
{
vlc_mutex_unlock( &p_httpt->file_lock );
msg_Err( p_httpt, "cannot unregister file" );
return;
}
if( p_file->i_ref > 0 )
{
httpd_connection_t *p_con;
/* force closing all connection for this file */
msg_Err( p_httpt, "closing all client connection" );
vlc_mutex_lock( &p_httpt->connection_lock );
for( p_con = p_httpt->p_first_connection; p_con != NULL; )
{
httpd_connection_t *p_next;
p_next = p_con->p_next;
if( p_con->p_file == p_file )
{
httpd_ConnnectionClose( p_httpt, p_con );
}
p_con = p_next;
}
vlc_mutex_unlock( &p_httpt->connection_lock );
}
FREE( p_file->psz_file );
FREE( p_file->psz_mime );
if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
{
FREE( p_file->psz_user );
FREE( p_file->psz_password );
}
FREE( p_file->p_buffer );
FREE( p_file );
if( p_httpt->i_file_count == 1 )
{
FREE( p_httpt->file );
p_httpt->i_file_count = 0;
}
else
{
int i_move;
i_move = p_httpt->i_file_count - i - 1;
if( i_move > 0 )
{
memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
}
p_httpt->i_file_count--;
p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
}
vlc_mutex_unlock( &p_httpt->file_lock );
}
static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
{
_UnregisterFile( p_httpd->p_sys, p_file );
}
static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
{
_UnregisterFile( p_httpd->p_sys, p_stream );
}
static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
{
if( i_data <= 0 )
{
return( VLC_SUCCESS );
}
vlc_mutex_lock( &p_httpt->file_lock );
if( p_stream->i_buffer_size < p_stream->i_buffer_valid + i_data )
{
/* not enough room */
if( p_stream->i_buffer < p_stream->i_buffer_valid )
{
memmove( p_stream->p_buffer,
p_stream->p_buffer + p_stream->i_buffer,
p_stream->i_buffer_valid - p_stream->i_buffer );
}
p_stream->i_buffer_valid -= p_stream->i_buffer;
p_stream->i_buffer = 0;
}
i_data = __MIN( i_data, p_stream->i_buffer_size - p_stream->i_buffer_valid );
memcpy( p_stream->p_buffer + p_stream->i_buffer_valid, p_data, i_data );
p_stream->i_buffer_valid += i_data;
vlc_mutex_unlock( &p_httpt->file_lock );
return( VLC_SUCCESS );
}
static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
{
return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
}
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
static int httpd_page_401_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
{
char *p;
p = *pp_data = malloc( 1024 );
p += sprintf( p, "<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_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
{
char *p;
p = *pp_data = malloc( 1024 );
p += sprintf( p, "<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;
}
static int httpd_page_admin_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
{
httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
httpd_connection_t *p_con;
char *p;
int i;
/* FIXME FIXME do not use static size FIXME FIXME*/
p = *pp_data = malloc( 8096 );
p += sprintf( p, "<html>\n" );
p += sprintf( p, "<head>\n" );
p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
p += sprintf( p, "</head>\n" );
p += sprintf( p, "<body>\n" );
p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
p += sprintf( p, "<h2><center>Admin page</center></h2>\n" );
/* host list */
vlc_mutex_lock( &p_httpt->host_lock );
p += sprintf( p, "<h3>Host list</h3>\n" );
p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
p += sprintf( p, "<tr>\n<th>Host</th><th>Port</th><th>IP</th>\n</tr>\n" );
for( i = 0; i < p_httpt->i_host_count; i++ )
{
p += sprintf( p, "<tr>\n" );
p += sprintf( p, "<td>%s</td>\n", p_httpt->host[i]->psz_host_addr );
p += sprintf( p, "<td>%d</td>\n", p_httpt->host[i]->i_port );
p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_httpt->host[i]->sock.sin_addr ) );
p += sprintf( p, "</tr>\n" );
}
p += sprintf( p, "</table>\n" );
vlc_mutex_unlock( &p_httpt->host_lock );
/* file list */
/* XXX do not take lock on file_lock */
p += sprintf( p, "<h3>File list</h3>\n" );
p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
for( i = 0; i < p_httpt->i_file_count; i++ )
{
if( !p_httpt->file[i]->b_stream )
{
p += sprintf( p, "<tr>\n" );
p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
p += sprintf( p, "</tr>\n" );
}
}
p += sprintf( p, "</table>\n" );
/* stream list */
/* XXX do not take lock on file_lock */
p += sprintf( p, "<h3>Stream list</h3>\n" );
p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
for( i = 0; i < p_httpt->i_file_count; i++ )
{
if( p_httpt->file[i]->b_stream )
{
p += sprintf( p, "<tr>\n" );
p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
p += sprintf( p, "</tr>\n" );
}
}
p += sprintf( p, "</table>\n" );
/* connection list */
/* XXX do not take lock on connection_lock */
p += sprintf( p, "<h3>Connection list</h3>\n" );
p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
p += sprintf( p, "<tr>\n<th>IP</th><th>Requested File</th><th>Status</th>\n</tr>\n" );
for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
{
p += sprintf( p, "<tr>\n" );
p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_con->sock.sin_addr ) );
p += sprintf( p, "<td>%s</td>\n", p_con->psz_file );
p += sprintf( p, "<td>%d</td>\n", p_con->i_http_error );
p += sprintf( p, "</tr>\n" );
}
p += sprintf( p, "</table>\n" );
/* www.videolan.org */
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 void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
{
httpd_connection_t *p_con;
msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
/* create a new connection and link it */
p_con = malloc( sizeof( httpd_connection_t ) );
p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST;
p_con->fd = fd;
p_con->sock = *p_sock;
p_con->psz_file = NULL;
p_con->i_http_error = 0;
p_con->psz_user = NULL;
p_con->psz_password = NULL;
p_con->p_file = NULL;
p_con->i_buffer = 0;
p_con->i_buffer_size = 8096;
p_con->p_buffer = malloc( p_con->i_buffer_size );
p_con->p_next = NULL;
if( p_httpt->p_first_connection )
{
httpd_connection_t *p_last;
p_last = p_httpt->p_first_connection;
while( p_last->p_next )
{
p_last = p_last->p_next;
}
p_last->p_next = p_con;
p_con->p_prev = p_last;
}
else
{
p_con->p_prev = NULL;
p_httpt->p_first_connection = p_con;
}
p_httpt->i_connection_count++;
}
static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
{
msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
p_httpt->i_connection_count--;
/* first cut out from list */
if( p_con->p_prev )
{
p_con->p_prev->p_next = p_con->p_next;
}
else
{
p_httpt->p_first_connection = p_con->p_next;
}
if( p_con->p_next )
{
p_con->p_next->p_prev = p_con->p_prev;
}
if( p_con->p_file ) p_con->p_file->i_ref--;
FREE( p_con->psz_file );
FREE( p_con->p_buffer );
SOCKET_CLOSE( p_con->fd );
FREE( p_con->psz_user );
FREE( p_con->psz_password );
free( p_con );
}
static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
{
char *p = *pp_buffer;
int i;
while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
{
p++;
}
i = 0;
for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
{
word[i] = *p;
}
word[__MIN( i, i_word_max -1 )] = '\0';
*pp_buffer = p;
}
static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
{
char *p;
for( p = *pp_buffer; p < p_end; p++ )
{
if( p + 1 < p_end && *p == '\n' )
{
*pp_buffer = p + 1;
return VLC_SUCCESS;
}
if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
{
*pp_buffer = p + 2;
return VLC_SUCCESS;
}
}
*pp_buffer = p_end;
return VLC_EGENERIC;
}
//char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static void b64_decode( char *dest, char *src )
{
int i_level;
int last = 0;
int b64[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
};
for( i_level = 0; *src != '\0' > 0; src++ )
{
int c;
c = b64[(unsigned int)*src];
if( c == -1 )
{
src++;
continue;
}
switch( i_level )
{
case 0:
i_level++;
break;
case 1:
*dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
i_level++;
break;
case 2:
*dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
i_level++;
break;
case 3:
*dest++ = ( ( last &0x03 ) << 6 ) | c;
i_level = 0;
}
last = c;
}
*dest = '\0';
}
static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
{
char *psz_status;
char *p, *p_end;
int i;
char command[32];
char url[1024];
char version[32];
char user[512] = "";
char password[512] = "";
//msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
p = p_con->p_buffer;
p_end = p + strlen( p ) + 1;
httpd_RequestGetWord( command, 32, &p, p_end );
httpd_RequestGetWord( url, 1024, &p, p_end );
httpd_RequestGetWord( version, 32, &p, p_end );
//msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
if( strcmp( command, "GET" ) )
{
/* unimplemented */
p_con->psz_file = strdup( "/501.html" );
p_con->i_http_error = 501;
goto create_header;
}
if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
{
p_con->psz_file = strdup( "/505.html" );
p_con->i_http_error = 505;
goto create_header;
}
/* parse headers */
for( ;; )
{
char header[1024];
if( httpd_RequestNextLine( &p, p_end ) )
{
//msg_Dbg( p_httpt, "failled new line" );
break;;
}
//msg_Dbg( p_httpt, "new line=%s", p );
httpd_RequestGetWord( header, 1024, &p, p_end );
if( !strcmp( header, "Authorization:" ) )
{
char method[32];
httpd_RequestGetWord( method, 32, &p, p_end );
if( !strcasecmp( method, "BASIC" ) )
{
char basic[1024];
char decoded[1024];
httpd_RequestGetWord( basic, 1024, &p, p_end );
//msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
b64_decode( decoded, basic );
//msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
if( strchr( decoded, ':' ) )
{
char *p = strchr( decoded, ':' );
p[0] = '\0'; p++;
strcpy( user, decoded );
strcpy( password, p );
}
}
}
}
p_con->psz_file = strdup( url );
p_con->i_http_error = 200;
create_header:
//msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
FREE( p_con->p_buffer );
p_con->i_buffer = 0;
p_con->i_buffer_size = 0;
//vlc_mutex_lock( &p_httpt->file_lock );
search_file:
/* search file */
for( i = 0, p_con->p_file = NULL; i < p_httpt->i_file_count; i++ )
{
if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
{
p_con->p_file = p_httpt->file[i];
}
}
if( !p_con->p_file )
{
p_con->psz_file = strdup( "/404.html" );
p_con->i_http_error = 404;
/* XXX be sure that "/404.html" exist else ... */
goto search_file;
}
if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
{
if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) )
{
p_con->psz_file = strdup( "/401.html" );
strcpy( user, p_con->p_file->psz_user );
p_con->i_http_error = 401;
/* XXX do not put password on 404 else ... */
goto search_file;
}
}
p_con->p_file->i_ref++;
// vlc_mutex_unlock( &p_httpt->file_lock );
switch( p_con->i_http_error )
{
case 200:
psz_status = "OK";
break;
case 401:
psz_status = "Authorization Required";
break;
default:
psz_status = "Unknown";
break;
}
p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
p_con->i_buffer_size = 4096;
p_con->i_buffer = 0;
p = p_con->p_buffer = malloc( p_con->i_buffer_size );
p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
if( p_con->i_http_error == 401 )
{
p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
}
p += sprintf( p, "\r\n" );
p_con->i_buffer_size = strlen( p_con->p_buffer ) + 1;
//msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
}
#define HTTPD_STREAM_PACKET 1300
static void httpd_Thread( httpd_sys_t *p_httpt )
{
httpd_file_t *p_page_admin;
httpd_file_t *p_page_401;
httpd_file_t *p_page_404;
httpd_connection_t *p_con;
vlc_bool_t b_wait;
msg_Info( p_httpt, "httpd started" );
p_page_401 = _RegisterFile( p_httpt,
"/401.html", "text/html",
NULL, NULL,
httpd_page_401_fill,
(httpd_file_callback_args_t*)NULL );
p_page_404 = _RegisterFile( p_httpt,
"/404.html", "text/html",
NULL, NULL,
httpd_page_404_fill,
(httpd_file_callback_args_t*)NULL );
p_page_admin = _RegisterFile( p_httpt,
"/admin.html", "text/html",
"admin", "salut",
httpd_page_admin_fill,
(httpd_file_callback_args_t*)p_httpt );
while( !p_httpt->b_die )
{
int i;
if( p_httpt->i_host_count <= 0 )
{
msleep( 100 * 1000 );
continue;
}
vlc_mutex_lock( &p_httpt->host_lock );
/* accept/refuse new connection */
for( i = 0; i < p_httpt->i_host_count; i++ )
{
int i_sock_size = sizeof( struct sockaddr_in );
struct sockaddr_in sock;
int fd;
fd = accept( p_httpt->host[i]->fd, &sock, &i_sock_size );
if( fd > 0 )
{
fcntl( fd, F_SETFL, O_NONBLOCK );
if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
{
msg_Warn( p_httpt, "max connection reached" );
SOCKET_CLOSE( fd );
continue;
}
/* create a new connection and link it */
httpd_ConnnectionNew( p_httpt, fd, &sock );
}
}
vlc_mutex_unlock( &p_httpt->host_lock );
vlc_mutex_lock( &p_httpt->file_lock );
/* now do work for all connections */
for( p_con = p_httpt->p_first_connection; p_con != NULL; )
{
if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
{
int i_len;
/* read data */
i_len = recv( p_con->fd,
p_con->p_buffer + p_con->i_buffer,
p_con->i_buffer_size - p_con->i_buffer, 0 );
if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
( i_len == 0 ) )
{
httpd_connection_t *p_next = p_con->p_next;
httpd_ConnnectionClose( p_httpt, p_con );
p_con = p_next;
}
else if( i_len > 0 )
{
uint8_t *ptr;
p_con->i_buffer += i_len;
ptr = p_con->p_buffer + p_con->i_buffer;
if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
p_con->i_buffer >= p_con->i_buffer_size )
{
p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
httpd_ConnectionParseRequest( p_httpt, p_con );
}
p_con = p_con->p_next;
}
else
{
p_con = p_con->p_next;
}
continue; /* just for clarity */
}
else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
{
int i_len;
/* write data */
i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
// msg_Warn( p_httpt, "on %d send %d bytes %s", p_con->i_buffer_size, i_len, p_con->p_buffer + p_con->i_buffer );
if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
( i_len == 0 ) )
{
httpd_connection_t *p_next = p_con->p_next;
httpd_ConnnectionClose( p_httpt, p_con );
p_con = p_next;
}
else if( i_len > 0 )
{
p_con->i_buffer += i_len;
if( p_con->i_buffer >= p_con->i_buffer_size )
{
if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
{
p_con->i_buffer_size = 0;
p_con->i_buffer = 0;
FREE( p_con->p_buffer );
if( !p_con->p_file->b_stream )
{
p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
p_con->p_file->pf_fill( p_con->p_file->p_sys, &p_con->p_buffer, &p_con->i_buffer_size );
}
else
{
p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
}
p_con = p_con->p_next;
}
else
{
httpd_connection_t *p_next = p_con->p_next;
httpd_ConnnectionClose( p_httpt, p_con );
p_con = p_next;
}
}
else
{
p_con = p_con->p_next;
}
}
else
{
p_con = p_con->p_next;
}
continue; /* just for clarity */
}
else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
{
httpd_file_t *p_file = p_con->p_file;
int i_len;
//msg_Dbg( p_httpt, "buffer=%d buffer_size=%d", p_file->i_buffer, p_file->i_buffer_size );
if( p_file->i_buffer < p_file->i_buffer_valid )
{
int i_write;
/* write data */
i_write = __MIN( p_file->i_buffer_valid - p_file->i_buffer, HTTPD_STREAM_PACKET );
i_len = send( p_con->fd, p_file->p_buffer + p_file->i_buffer, i_write, 0 );
if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
( i_len == 0 ) )
{
httpd_connection_t *p_next = p_con->p_next;
httpd_ConnnectionClose( p_httpt, p_con );
p_con = p_next;
}
else
{
p_con = p_con->p_next;
}
}
else
{
p_con = p_con->p_next;
}
continue; /* just for clarity */
}
else
{
msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
p_con = p_con->p_next;
}
} /* for over connection */
b_wait = VLC_TRUE;
/* update position for stream based file */
for( i = 0; i < p_httpt->i_file_count; i++ )
{
if( p_httpt->file[i]->b_stream )
{
p_httpt->file[i]->i_buffer += __MIN( p_httpt->file[i]->i_buffer_valid - p_httpt->file[i]->i_buffer,
HTTPD_STREAM_PACKET );
if( p_httpt->file[i]->i_buffer < p_httpt->file[i]->i_buffer_valid )
{
/* there is data */
b_wait = VLC_FALSE;
}
}
}
vlc_mutex_unlock( &p_httpt->file_lock );
if( b_wait ) msleep( 100 );
}
msg_Info( p_httpt, "httpd stopped" );
_UnregisterFile( p_httpt, p_page_401 );
_UnregisterFile( p_httpt, p_page_404 );
_UnregisterFile( p_httpt, p_page_admin );
}
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