* modules/access_output/http : http output.
authorLaurent Aimar <fenrir@videolan.org>
Sun, 23 Feb 2003 19:05:22 +0000 (19:05 +0000)
committerLaurent Aimar <fenrir@videolan.org>
Sun, 23 Feb 2003 19:05:22 +0000 (19:05 +0000)
 * httpd : mini http server (be carefull about security issue...)

include/httpd.h [new file with mode: 0644]
modules/access_output/http.c [new file with mode: 0644]
modules/misc/httpd.c [new file with mode: 0644]

diff --git a/include/httpd.h b/include/httpd.h
new file mode 100644 (file)
index 0000000..0f13e05
--- /dev/null
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ * httpd.h
+ *****************************************************************************
+ * Copyright (C) 2001-2003 VideoLAN
+ * $Id: httpd.h,v 1.1 2003/02/23 19:05:22 fenrir Exp $
+ *
+ * Authors: Laurent Aimar <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 );
+}
+
+
+
diff --git a/modules/access_output/http.c b/modules/access_output/http.c
new file mode 100644 (file)
index 0000000..2286219
--- /dev/null
@@ -0,0 +1,254 @@
+/*****************************************************************************
+ * http.c
+ *****************************************************************************
+ * Copyright (C) 2001-2003 VideoLAN
+ * $Id: http.c,v 1.1 2003/02/23 19:05:22 fenrir Exp $
+ *
+ * Authors: Laurent Aimar <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 );
+}
+
diff --git a/modules/misc/httpd.c b/modules/misc/httpd.c
new file mode 100644 (file)
index 0000000..6bd9a63
--- /dev/null
@@ -0,0 +1,1545 @@
+/*****************************************************************************
+ * httpd.c
+ *****************************************************************************
+ * Copyright (C) 2001-2003 VideoLAN
+ * $Id: httpd.c,v 1.1 2003/02/23 19:05:22 fenrir Exp $
+ *
+ * Authors: Laurent Aimar <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 );
+}
+