* stream_output.* : added a flags variable to sout_buffer_t, allowing to
[vlc.git] / modules / misc / httpd.c
1 /*****************************************************************************
2  * httpd.c
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: httpd.c,v 1.3 2003/02/25 17:17:43 fenrir Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <vlc/vlc.h>
35 #include "httpd.h"
36
37 #ifdef HAVE_UNISTD_H
38 #   include <unistd.h>
39 #elif defined( _MSC_VER ) && defined( _WIN32 ) && !defined( UNDER_CE )
40 #   include <io.h>
41 #endif
42
43 #if defined( UNDER_CE )
44 #   include <winsock.h>
45 #elif defined( WIN32 )
46 #   include <winsock2.h>
47 #   include <ws2tcpip.h>
48 #   ifndef IN_MULTICAST
49 #       define IN_MULTICAST(a) IN_CLASSD(a)
50 #   endif
51 #else
52 #   include <netdb.h>                                         /* hostent ... */
53 #   include <sys/socket.h>
54 #   include <netinet/in.h>
55 #   ifdef HAVE_ARPA_INET_H
56 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
57 #   endif
58 #endif
59
60 #include "network.h"
61
62 #ifndef INADDR_ANY
63 #   define INADDR_ANY  0x00000000
64 #endif
65 #ifndef INADDR_NONE
66 #   define INADDR_NONE 0xFFFFFFFF
67 #endif
68
69 #define LISTEN_BACKLOG  100
70 #define HTTPD_MAX_CONNECTION    1024
71
72
73 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
74
75 #if defined( WIN32 ) || defined( UNDER_CE )
76 #define SOCKET_CLOSE(a)    closesocket(a)
77 #else
78 #define SOCKET_CLOSE(a)    close(a)
79 #endif
80
81 /*****************************************************************************
82  * Exported prototypes
83  *****************************************************************************/
84 static int              Open   ( vlc_object_t * );
85 static void             Close  ( vlc_object_t * );
86
87 /*****************************************************************************
88  * Module descriptor
89  *****************************************************************************/
90 vlc_module_begin();
91     set_description( _("HTTP 1.0 daemon") );
92     set_capability( "httpd", 42 );
93     set_callbacks( Open, Close );
94 vlc_module_end();
95
96 /*****************************************************************************
97  * Prototypes
98  *****************************************************************************/
99 static httpd_host_t     *RegisterHost   ( httpd_t *, char *, int );
100 static void             UnregisterHost  ( httpd_t *, httpd_host_t * );
101
102 static httpd_file_t     *RegisterFile   ( httpd_t *,
103                                           char *psz_file, char *psz_mime,
104                                           char *psz_user, char *psz_password,
105                                           httpd_file_callback pf_fill,
106                                           httpd_file_callback_args_t *p_args );
107 static void             UnregisterFile  ( httpd_t *, httpd_file_t * );
108
109 //#define httpd_stream_t              httpd_file_t
110 static httpd_stream_t   *RegisterStream ( httpd_t *,
111                                          char *psz_file, char *psz_mime,
112                                          char *psz_user, char *psz_password );
113 static int              SendStream      ( httpd_t *, httpd_stream_t *, uint8_t *, int );
114 static int              HeaderStream    ( httpd_t *, httpd_stream_t *, uint8_t *, int );
115 static void             UnregisterStream( httpd_t *, httpd_stream_t* );
116
117 /*****************************************************************************
118  * Internal definitions
119  *****************************************************************************/
120 struct httpd_host_t
121 {
122     int    i_ref;
123
124     char   *psz_host_addr;
125     int    i_port;
126
127     struct sockaddr_in sock;
128     int    fd;
129
130 };
131
132 #define HTTPD_AUTHENTICATE_NONE     0
133 #define HTTPD_AUTHENTICATE_BASIC    1
134
135 //typedef httpd_file_t httpd_stream_t;
136
137 struct httpd_file_t
138 {
139     int         i_ref;
140
141     char        *psz_file;
142     char        *psz_mime;
143
144
145     int         i_authenticate_method;
146         char        *psz_user;          /* NULL if no auth */
147         char        *psz_password;      /* NULL if no auth */
148
149     vlc_bool_t  b_stream;               /* if false: httpd will retreive data by a callback
150                                               true:  it's up to the program to give data to httpd */
151     void                *p_sys;         /* provided for user */
152     httpd_file_callback pf_fill;       /* it should allocate and fill *pp_data and *pi_data */
153
154     /* private */
155
156     /* circular buffer for stream only */
157     int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
158     uint8_t     *p_buffer;          /* buffer */
159     int64_t     i_buffer_pos;       /* absolute position from begining */
160     int         i_buffer_last_pos;  /* a new connection will start with that */
161
162     /* data to be send at connection time (if any) */
163     int         i_header_size;
164     uint8_t     *p_header;
165 };
166
167
168 #define HTTPD_CONNECTION_RECEIVING_REQUEST      1
169 #define HTTPD_CONNECTION_SENDING_HEADER         2
170 #define HTTPD_CONNECTION_SENDING_FILE           3
171 #define HTTPD_CONNECTION_SENDING_STREAM         4
172
173 typedef struct httpd_connection_s
174 {
175     struct httpd_connection_s *p_next;
176     struct httpd_connection_s *p_prev;
177
178     struct sockaddr_in sock;
179     int    fd;
180
181     int    i_state;
182
183     char    *psz_file;      // file to be send
184     int     i_http_error;   // error to be send with the file
185     char    *psz_user;      // if Authorization in the request header
186     char    *psz_password;
187
188     httpd_file_t    *p_file;
189
190     /* used while sending header and file */
191     int     i_buffer_size;
192     uint8_t *p_buffer;
193     int     i_buffer;            /* private */
194
195     /* used for stream */
196     int64_t i_stream_pos;   /* absolute pos in stream */
197 } httpd_connection_t;
198
199 /*
200  * The httpd thread
201  */
202 struct httpd_sys_t
203 {
204     VLC_COMMON_MEMBERS
205
206     vlc_mutex_t             host_lock;
207     volatile int            i_host_count;
208     httpd_host_t            **host;
209
210     vlc_mutex_t             file_lock;
211     int                     i_file_count;
212     httpd_file_t            **file;
213
214     vlc_mutex_t             connection_lock;
215     int                     i_connection_count;
216     httpd_connection_t      *p_first_connection;
217 };
218
219 static void httpd_Thread( httpd_sys_t *p_httpt );
220 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
221 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
222
223 /*****************************************************************************
224  * Open:
225  *****************************************************************************/
226
227 static int Open( vlc_object_t *p_this )
228 {
229     httpd_t     *p_httpd = (httpd_t*)p_this;
230     httpd_sys_t *p_httpt;
231
232     /* Launch httpt thread */
233     if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
234     {
235         msg_Err( p_this, "out of memory" );
236         return( VLC_EGENERIC );
237     }
238
239     p_httpt->b_die  = 0;
240     p_httpt->b_error= 0;
241
242     /* init httpt_t structure */
243     vlc_mutex_init( p_httpd, &p_httpt->host_lock );
244     p_httpt->i_host_count = 0;
245     p_httpt->host = NULL;
246
247     vlc_mutex_init( p_httpd, &p_httpt->file_lock );
248     p_httpt->i_file_count = 0;
249     p_httpt->file = NULL;
250
251     vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
252     p_httpt->i_connection_count = 0;
253     p_httpt->p_first_connection = NULL;
254
255     /* start the thread */
256     if( vlc_thread_create( p_httpt, "httpd thread",
257                            httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
258     {
259         msg_Err( p_this, "cannot spawn http thread" );
260
261         vlc_mutex_destroy( &p_httpt->host_lock );
262         vlc_mutex_destroy( &p_httpt->file_lock );
263         vlc_mutex_destroy( &p_httpt->connection_lock );
264
265         vlc_object_destroy( p_httpt );
266         return( VLC_EGENERIC );
267     }
268
269     msg_Info( p_httpd, "http thread launched" );
270
271     p_httpd->p_sys = p_httpt;
272     p_httpd->pf_register_host   = RegisterHost;
273     p_httpd->pf_unregister_host = UnregisterHost;
274     p_httpd->pf_register_file   = RegisterFile;
275     p_httpd->pf_unregister_file = UnregisterFile;
276     p_httpd->pf_register_stream = RegisterStream;
277     p_httpd->pf_header_stream   = HeaderStream;
278     p_httpd->pf_send_stream     = SendStream;
279     p_httpd->pf_unregister_stream=UnregisterStream;
280
281     return( VLC_SUCCESS );
282 }
283
284 /*****************************************************************************
285  * Close: close the target
286  *****************************************************************************/
287 static void Close( vlc_object_t * p_this )
288 {
289     httpd_t     *p_httpd = (httpd_t*)p_this;
290     httpd_sys_t *p_httpt = p_httpd->p_sys;
291
292     httpd_connection_t *p_con;
293     int i;
294
295     p_httpt->b_die = 1;
296     vlc_thread_join( p_httpt );
297
298     /* first close all host */
299     vlc_mutex_destroy( &p_httpt->host_lock );
300     if( p_httpt->i_host_count )
301     {
302         msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
303     }
304     for( i = 0; i < p_httpt->i_host_count; i++ )
305     {
306 #define p_host p_httpt->host[i]
307         FREE( p_host->psz_host_addr );
308         SOCKET_CLOSE( p_host->fd );
309
310         FREE( p_host );
311 #undef p_host
312     }
313     FREE( p_httpt->host );
314
315     /* now all file */
316     vlc_mutex_destroy( &p_httpt->file_lock );
317     if( p_httpt->i_file_count )
318     {
319         msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
320     }
321     for( i = 0; i < p_httpt->i_file_count; i++ )
322     {
323 #define p_file p_httpt->file[i]
324         FREE( p_file->psz_file );
325         FREE( p_file->psz_mime );
326         if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
327         {
328             FREE( p_file->psz_user );
329             FREE( p_file->psz_password );
330         }
331         FREE( p_file->p_buffer );
332
333         FREE( p_file );
334 #undef p_file
335     }
336     FREE( p_httpt->file );
337
338     /* andd close all connection */
339     vlc_mutex_destroy( &p_httpt->connection_lock );
340     if( p_httpt->i_connection_count )
341     {
342         msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
343     }
344     while( ( p_con = p_httpt->p_first_connection ) )
345     {
346         httpd_ConnnectionClose( p_httpt, p_con );
347     }
348
349     msg_Info( p_httpd, "httpd instance closed" );
350     vlc_object_destroy( p_httpt );
351 }
352
353
354 /****************************************************************************
355  ****************************************************************************
356  ***
357  ***
358  ****************************************************************************
359  ****************************************************************************/
360 static int BuildAddr( struct sockaddr_in * p_socket,
361                       const char * psz_address, int i_port )
362 {
363     /* Reset struct */
364     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
365     p_socket->sin_family = AF_INET;                                /* family */
366     p_socket->sin_port = htons( (uint16_t)i_port );
367     if( !*psz_address )
368     {
369         p_socket->sin_addr.s_addr = INADDR_ANY;
370     }
371     else
372     {
373         struct hostent    * p_hostent;
374
375         /* Try to convert address directly from in_addr - this will work if
376          * psz_address is dotted decimal. */
377 #ifdef HAVE_ARPA_INET_H
378         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
379 #else
380         p_socket->sin_addr.s_addr = inet_addr( psz_address );
381         if( p_socket->sin_addr.s_addr == INADDR_NONE )
382 #endif
383         {
384             /* We have a fqdn, try to find its address */
385             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
386             {
387                 return( -1 );
388             }
389
390             /* Copy the first address of the host in the socket address */
391             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
392                      p_hostent->h_length );
393         }
394     }
395     return( 0 );
396 }
397
398
399 /*
400  * listen on a host for a httpd instance
401  */
402
403 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
404 {
405     httpd_host_t    *p_host;
406     struct sockaddr_in  sock;
407     int i;
408     int fd = -1;
409     int i_opt;
410 #if !defined( WIN32 ) && !defined( UNDER_CE )
411     int i_flags;
412 #endif
413
414     if( BuildAddr( &sock, psz_host_addr, i_port ) )
415     {
416         msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
417         return NULL;
418     }
419
420     /* is it already declared ? */
421     vlc_mutex_lock( &p_httpt->host_lock );
422     for( i = 0; i < p_httpt->i_host_count; i++ )
423     {
424         if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
425             p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr )
426         {
427             break;
428         }
429     }
430
431     if( i < p_httpt->i_host_count )
432     {
433         /* yes, increment ref count and succed */
434         p_httpt->host[i]->i_ref++;
435         vlc_mutex_unlock( &p_httpt->host_lock );
436         return NULL;
437     }
438
439     /* need to add a new listening socket */
440
441     /* open socket */
442     fd = socket( AF_INET, SOCK_STREAM, 0 );
443     if( fd < 0 )
444     {
445         msg_Err( p_httpt, "cannot open socket" );
446         goto socket_failed;
447     }
448     /* reuse socket */
449     i_opt = 1;
450     if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
451                     (void *) &i_opt, sizeof( i_opt ) ) < 0 )
452     {
453         msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
454     }
455     /* bind it */
456     if( bind( fd, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 )
457     {
458         msg_Err( p_httpt, "cannot bind socket" );
459         goto socket_failed;
460     }
461     /* set to non-blocking */
462 #if defined( WIN32 ) || defined( UNDER_CE )
463     {
464         unsigned long i_dummy = 1;
465         if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
466         {
467             msg_Err( p_httpt, "cannot set socket to non-blocking mode" );
468             goto socket_failed;
469         }
470     }
471 #else
472     if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
473     {
474         msg_Err( p_httpt, "cannot F_GETFL socket" );
475         goto socket_failed;
476     }
477     if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
478     {
479         msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
480         goto socket_failed;
481     }
482 #endif
483     /* listen */
484     if( listen( fd, LISTEN_BACKLOG ) < 0 )
485     {
486         msg_Err( p_httpt, "cannot listen socket" );
487         goto socket_failed;
488     }
489
490     if( p_httpt->host )
491     {
492         p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *)  * ( p_httpt->i_host_count + 1 ) );
493     }
494     else
495     {
496         p_httpt->host = malloc( sizeof( httpd_host_t *) );
497     }
498     p_host                = malloc( sizeof( httpd_host_t ) );
499     p_host->i_ref         = 1;
500     p_host->psz_host_addr = strdup( psz_host_addr );
501     p_host->i_port        = i_port;
502     p_host->sock          = sock;
503     p_host->fd            = fd;
504
505     p_httpt->host[p_httpt->i_host_count++] = p_host;
506     vlc_mutex_unlock( &p_httpt->host_lock );
507
508     return p_host;
509
510 socket_failed:
511     vlc_mutex_unlock( &p_httpt->host_lock );
512     if( fd >= 0 )
513     {
514         SOCKET_CLOSE( fd );
515     }
516     return NULL;
517 }
518 static httpd_host_t     *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
519 {
520     return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
521 }
522
523 /*
524  * remove a listening host for an httpd instance
525  */
526 static void            _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
527 {
528     int i;
529
530     vlc_mutex_lock( &p_httpt->host_lock );
531     for( i = 0; i < p_httpt->i_host_count; i++ )
532     {
533         if( p_httpt->host[i] == p_host )
534         {
535             break;
536         }
537     }
538     if( i >= p_httpt->i_host_count )
539     {
540         vlc_mutex_unlock( &p_httpt->host_lock );
541         msg_Err( p_httpt, "cannot unregister host" );
542         return;
543     }
544
545     p_host->i_ref--;
546
547     if( p_host->i_ref > 0 )
548     {
549         /* still in use */
550         vlc_mutex_unlock( &p_httpt->host_lock );
551         return;
552     }
553
554     /* no more used */
555     FREE( p_host->psz_host_addr );
556     SOCKET_CLOSE( p_host->fd );
557
558     FREE( p_host );
559
560     if( p_httpt->i_host_count <= 1 )
561     {
562         FREE( p_httpt->host );
563         p_httpt->i_host_count = 0;
564     }
565     else
566     {
567         int i_move;
568
569         i_move = p_httpt->i_host_count - i - 1;
570
571         if( i_move > 0 )
572         {
573             memmove( &p_httpt->host[i],
574                      &p_httpt->host[i+1],
575                      i_move * sizeof( httpd_host_t * ) );
576         }
577
578         p_httpt->i_host_count--;
579         p_httpt->host = realloc( p_httpt->host,
580                                  p_httpt->i_host_count * sizeof( httpd_host_t * ) );
581     }
582
583     vlc_mutex_unlock( &p_httpt->host_lock );
584 }
585 static void             UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
586 {
587     _UnregisterHost( p_httpd->p_sys, p_host );
588 }
589
590
591 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
592 {
593     /* add a new file */
594     if( p_httpt->i_file_count )
595     {
596         p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *)  * ( p_httpt->i_file_count + 1 ) );
597     }
598     else
599     {
600         p_httpt->file = malloc( sizeof( httpd_file_t *) );
601     }
602
603     p_httpt->file[p_httpt->i_file_count++] = p_file;
604 }
605
606 static httpd_file_t    *_RegisterFile( httpd_sys_t *p_httpt,
607                                        char *psz_file, char *psz_mime,
608                                        char *psz_user, char *psz_password,
609                                        httpd_file_callback pf_fill,
610                                        httpd_file_callback_args_t *p_args )
611 {
612     httpd_file_t    *p_file;
613     int i;
614
615     vlc_mutex_lock( &p_httpt->file_lock );
616     for( i = 0; i < p_httpt->i_file_count; i++ )
617     {
618         if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
619         {
620             break;
621         }
622     }
623     if( i < p_httpt->i_file_count )
624     {
625         vlc_mutex_unlock( &p_httpt->file_lock );
626         msg_Err( p_httpt, "%s already registered", psz_file );
627         return NULL;
628     }
629
630     p_file              = malloc( sizeof( httpd_file_t ) );
631     p_file->i_ref       = 0;
632     p_file->psz_file    = strdup( psz_file );
633     p_file->psz_mime    = strdup( psz_mime );
634     if( psz_user && *psz_user )
635     {
636         p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
637         p_file->psz_user              = strdup( psz_user );
638         p_file->psz_password          = strdup( psz_password );
639     }
640     else
641     {
642         p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
643         p_file->psz_user              = NULL;
644         p_file->psz_password          = NULL;
645     }
646
647     p_file->b_stream          = VLC_FALSE;
648     p_file->p_sys             = p_args;
649     p_file->pf_fill           = pf_fill;
650
651     p_file->i_buffer_size     = 0;
652     p_file->i_buffer_last_pos = 0;
653     p_file->i_buffer_pos      = 0;
654     p_file->p_buffer          = NULL;
655
656     p_file->i_header_size     = 0;
657     p_file->p_header          = NULL;
658
659     __RegisterFile( p_httpt, p_file );
660
661     vlc_mutex_unlock( &p_httpt->file_lock );
662
663     return p_file;
664 }
665 static httpd_file_t     *RegisterFile( httpd_t *p_httpd,
666                                        char *psz_file, char *psz_mime,
667                                        char *psz_user, char *psz_password,
668                                        httpd_file_callback pf_fill,
669                                        httpd_file_callback_args_t *p_args )
670 {
671     return( _RegisterFile( p_httpd->p_sys,
672                            psz_file, psz_mime, psz_user, psz_password,
673                            pf_fill, p_args ) );
674 }
675
676 static httpd_stream_t  *_RegisterStream( httpd_sys_t *p_httpt,
677                                          char *psz_file, char *psz_mime,
678                                          char *psz_user, char *psz_password )
679 {
680     httpd_stream_t    *p_stream;
681     int i;
682
683     vlc_mutex_lock( &p_httpt->file_lock );
684     for( i = 0; i < p_httpt->i_file_count; i++ )
685     {
686         if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
687         {
688             break;
689         }
690     }
691     if( i < p_httpt->i_file_count )
692     {
693         vlc_mutex_unlock( &p_httpt->file_lock );
694         msg_Err( p_httpt, "%s already registered", psz_file );
695         return NULL;
696     }
697
698     p_stream              = malloc( sizeof( httpd_stream_t ) );
699     p_stream->i_ref       = 0;
700     p_stream->psz_file    = strdup( psz_file );
701     p_stream->psz_mime    = strdup( psz_mime );
702     if( psz_user && *psz_user )
703     {
704         p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
705         p_stream->psz_user              = strdup( psz_user );
706         p_stream->psz_password          = strdup( psz_password );
707     }
708     else
709     {
710         p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
711         p_stream->psz_user              = NULL;
712         p_stream->psz_password          = NULL;
713     }
714
715     p_stream->b_stream        = VLC_TRUE;
716     p_stream->p_sys           = NULL;
717     p_stream->pf_fill         = NULL;
718
719     p_stream->i_buffer_size   = 1024*1024*10;
720     p_stream->i_buffer_pos      = 0;
721     p_stream->i_buffer_last_pos = 0;
722     p_stream->p_buffer        = malloc( p_stream->i_buffer_size );
723
724     p_stream->i_header_size   = 0;
725     p_stream->p_header        = NULL;
726
727     __RegisterFile( p_httpt, p_stream );
728
729     vlc_mutex_unlock( &p_httpt->file_lock );
730
731     return p_stream;
732 }
733 static httpd_stream_t   *RegisterStream( httpd_t *p_httpd,
734                                          char *psz_file, char *psz_mime,
735                                          char *psz_user, char *psz_password )
736 {
737     return( _RegisterStream( p_httpd->p_sys,
738                              psz_file, psz_mime, psz_user, psz_password ) );
739 }
740
741 static void            _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
742 {
743     int i;
744
745     vlc_mutex_lock( &p_httpt->file_lock );
746     for( i = 0; i < p_httpt->i_file_count; i++ )
747     {
748         if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
749         {
750             break;
751         }
752     }
753     if( i >= p_httpt->i_file_count )
754     {
755         vlc_mutex_unlock( &p_httpt->file_lock );
756         msg_Err( p_httpt, "cannot unregister file" );
757         return;
758     }
759
760     if( p_file->i_ref > 0 )
761     {
762         httpd_connection_t *p_con;
763         /* force closing all connection for this file */
764         msg_Err( p_httpt, "closing all client connection" );
765
766         vlc_mutex_lock( &p_httpt->connection_lock );
767         for( p_con = p_httpt->p_first_connection; p_con != NULL; )
768         {
769             httpd_connection_t *p_next;
770
771             p_next = p_con->p_next;
772             if( p_con->p_file == p_file )
773             {
774                 httpd_ConnnectionClose( p_httpt, p_con );
775             }
776             p_con = p_next;
777         }
778         vlc_mutex_unlock( &p_httpt->connection_lock );
779     }
780
781     FREE( p_file->psz_file );
782     FREE( p_file->psz_mime );
783     if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
784     {
785         FREE( p_file->psz_user );
786         FREE( p_file->psz_password );
787     }
788     FREE( p_file->p_buffer );
789     FREE( p_file->p_header );
790
791     FREE( p_file );
792
793
794     if( p_httpt->i_file_count == 1 )
795     {
796         FREE( p_httpt->file );
797         p_httpt->i_file_count = 0;
798     }
799     else
800     {
801         int i_move;
802
803         i_move = p_httpt->i_file_count - i - 1;
804         if( i_move > 0  )
805         {
806             memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
807         }
808         p_httpt->i_file_count--;
809         p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
810     }
811
812     vlc_mutex_unlock( &p_httpt->file_lock );
813 }
814 static void            UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
815 {
816     _UnregisterFile( p_httpd->p_sys, p_file );
817 }
818
819 static void             UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
820 {
821     _UnregisterFile( p_httpd->p_sys, p_stream );
822 }
823
824
825
826 static int             _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
827 {
828     int i_count;
829     int i_pos;
830
831     if( i_data <= 0 || p_data == NULL )
832     {
833         return( VLC_SUCCESS );
834     }
835     //fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos );
836
837     vlc_mutex_lock( &p_httpt->file_lock );
838
839     /* save this pointer (to be used by new connection) */
840     p_stream->i_buffer_last_pos = p_stream->i_buffer_pos;
841
842     i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size;
843     i_count = i_data;
844     while( i_count > 0)
845     {
846         int i_copy;
847
848         i_copy = __MIN( i_data, p_stream->i_buffer_size - i_pos );
849
850         memcpy( &p_stream->p_buffer[i_pos],
851                 p_data,
852                 i_copy );
853
854         i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size;
855         i_count -= i_copy;
856         p_data += i_copy;
857     }
858
859     p_stream->i_buffer_pos += i_data;
860     vlc_mutex_unlock( &p_httpt->file_lock );
861
862     return( VLC_SUCCESS );
863 }
864 static int             SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
865 {
866     return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
867 }
868
869 static int             HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
870 {
871     httpd_sys_t *p_httpt = p_httpd->p_sys;
872
873     vlc_mutex_lock( &p_httpt->file_lock );
874
875     FREE( p_stream->p_header );
876     if( p_data == NULL || i_data <= 0 )
877     {
878         p_stream->i_header_size = 0;
879     }
880     else
881     {
882         p_stream->i_header_size = i_data;
883         p_stream->p_header = malloc( i_data );
884         memcpy( p_stream->p_header,
885                 p_data,
886                 i_data );
887     }
888     vlc_mutex_unlock( &p_httpt->file_lock );
889
890     return( VLC_SUCCESS );
891 }
892
893 /****************************************************************************/
894 /****************************************************************************/
895 /****************************************************************************/
896 /****************************************************************************/
897 /****************************************************************************/
898
899 static int  httpd_page_401_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
900 {
901     char *p;
902
903     p = *pp_data = malloc( 1024 );
904
905     p += sprintf( p, "<html>\n" );
906     p += sprintf( p, "<head>\n" );
907     p += sprintf( p, "<title>Error 401</title>\n" );
908     p += sprintf( p, "</head>\n" );
909     p += sprintf( p, "<body>\n" );
910     p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
911     p += sprintf( p, "<hr />\n" );
912     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
913     p += sprintf( p, "</body>\n" );
914     p += sprintf( p, "</html>\n" );
915
916     *pi_data = strlen( *pp_data ) + 1;
917
918     return VLC_SUCCESS;
919 }
920 static int  httpd_page_404_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
921 {
922     char *p;
923
924     p = *pp_data = malloc( 1024 );
925
926     p += sprintf( p, "<html>\n" );
927     p += sprintf( p, "<head>\n" );
928     p += sprintf( p, "<title>Error 404</title>\n" );
929     p += sprintf( p, "</head>\n" );
930     p += sprintf( p, "<body>\n" );
931     p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
932     p += sprintf( p, "<hr />\n" );
933     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
934     p += sprintf( p, "</body>\n" );
935     p += sprintf( p, "</html>\n" );
936
937     *pi_data = strlen( *pp_data ) + 1;
938
939     return VLC_SUCCESS;
940 }
941
942 static int  httpd_page_admin_fill( httpd_file_callback_args_t *p_args, uint8_t **pp_data, int *pi_data )
943 {
944     httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
945     httpd_connection_t *p_con;
946     char *p;
947     int i;
948
949     /* FIXME FIXME do not use static size FIXME FIXME*/
950     p = *pp_data = malloc( 8096 );
951
952     p += sprintf( p, "<html>\n" );
953     p += sprintf( p, "<head>\n" );
954     p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
955     p += sprintf( p, "</head>\n" );
956     p += sprintf( p, "<body>\n" );
957     p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
958     p += sprintf( p, "<h2><center>Admin page</center></h2>\n" );
959
960     /* host list */
961     vlc_mutex_lock( &p_httpt->host_lock );
962     p += sprintf( p, "<h3>Host list</h3>\n" );
963     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
964     p += sprintf( p, "<tr>\n<th>Host</th><th>Port</th><th>IP</th>\n</tr>\n" );
965
966     for( i = 0; i < p_httpt->i_host_count; i++ )
967     {
968         p += sprintf( p, "<tr>\n" );
969         p += sprintf( p, "<td>%s</td>\n", p_httpt->host[i]->psz_host_addr );
970         p += sprintf( p, "<td>%d</td>\n", p_httpt->host[i]->i_port );
971         p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_httpt->host[i]->sock.sin_addr ) );
972         p += sprintf( p, "</tr>\n" );
973     }
974     p += sprintf( p, "</table>\n" );
975     vlc_mutex_unlock( &p_httpt->host_lock );
976
977     /* file list */
978     /* XXX do not take lock on file_lock */
979     p += sprintf( p, "<h3>File list</h3>\n" );
980     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
981     p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
982
983     for( i = 0; i < p_httpt->i_file_count; i++ )
984     {
985         if( !p_httpt->file[i]->b_stream )
986         {
987             p += sprintf( p, "<tr>\n" );
988             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
989             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
990             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
991             p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
992             p += sprintf( p, "</tr>\n" );
993         }
994     }
995     p += sprintf( p, "</table>\n" );
996
997     /* stream list */
998     /* XXX do not take lock on file_lock */
999     p += sprintf( p, "<h3>Stream list</h3>\n" );
1000     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1001     p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
1002
1003     for( i = 0; i < p_httpt->i_file_count; i++ )
1004     {
1005         if( p_httpt->file[i]->b_stream )
1006         {
1007             p += sprintf( p, "<tr>\n" );
1008             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
1009             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
1010             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
1011             p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
1012             p += sprintf( p, "</tr>\n" );
1013         }
1014     }
1015     p += sprintf( p, "</table>\n" );
1016
1017     /* connection list */
1018     /* XXX do not take lock on connection_lock */
1019     p += sprintf( p, "<h3>Connection list</h3>\n" );
1020     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1021     p += sprintf( p, "<tr>\n<th>IP</th><th>Requested File</th><th>Status</th>\n</tr>\n" );
1022
1023     for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1024     {
1025         p += sprintf( p, "<tr>\n" );
1026         p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_con->sock.sin_addr ) );
1027         p += sprintf( p, "<td>%s</td>\n", p_con->psz_file );
1028         p += sprintf( p, "<td>%d</td>\n", p_con->i_http_error );
1029         p += sprintf( p, "</tr>\n" );
1030     }
1031     p += sprintf( p, "</table>\n" );
1032
1033
1034     /* www.videolan.org */
1035     p += sprintf( p, "<hr />\n" );
1036     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1037     p += sprintf( p, "</body>\n" );
1038     p += sprintf( p, "</html>\n" );
1039
1040     *pi_data = strlen( *pp_data ) + 1;
1041
1042     return VLC_SUCCESS;
1043 }
1044
1045
1046 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
1047 {
1048     httpd_connection_t *p_con;
1049
1050     msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
1051
1052     /* create a new connection and link it */
1053     p_con = malloc( sizeof( httpd_connection_t ) );
1054     p_con->i_state  = HTTPD_CONNECTION_RECEIVING_REQUEST;
1055     p_con->fd       = fd;
1056     p_con->sock     = *p_sock;
1057     p_con->psz_file = NULL;
1058     p_con->i_http_error = 0;
1059     p_con->psz_user = NULL;
1060     p_con->psz_password = NULL;
1061     p_con->p_file   = NULL;
1062
1063     p_con->i_buffer = 0;
1064     p_con->i_buffer_size = 8096;
1065     p_con->p_buffer = malloc( p_con->i_buffer_size );
1066
1067     p_con->i_stream_pos = 0; // updated by httpd_thread */
1068     p_con->p_next = NULL;
1069
1070     if( p_httpt->p_first_connection )
1071     {
1072         httpd_connection_t *p_last;
1073
1074         p_last = p_httpt->p_first_connection;
1075         while( p_last->p_next )
1076         {
1077             p_last = p_last->p_next;
1078         }
1079
1080         p_last->p_next = p_con;
1081         p_con->p_prev = p_last;
1082     }
1083     else
1084     {
1085         p_con->p_prev = NULL;
1086
1087         p_httpt->p_first_connection = p_con;
1088     }
1089
1090     p_httpt->i_connection_count++;
1091 }
1092
1093 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1094 {
1095     msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1096
1097     p_httpt->i_connection_count--;
1098     /* first cut out from list */
1099     if( p_con->p_prev )
1100     {
1101         p_con->p_prev->p_next = p_con->p_next;
1102     }
1103     else
1104     {
1105         p_httpt->p_first_connection = p_con->p_next;
1106     }
1107
1108     if( p_con->p_next )
1109     {
1110         p_con->p_next->p_prev = p_con->p_prev;
1111     }
1112
1113     if( p_con->p_file ) p_con->p_file->i_ref--;
1114     FREE( p_con->psz_file );
1115
1116     FREE( p_con->p_buffer );
1117     SOCKET_CLOSE( p_con->fd );
1118
1119
1120     FREE( p_con->psz_user );
1121     FREE( p_con->psz_password );
1122
1123     free( p_con );
1124 }
1125
1126 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1127 {
1128     char *p = *pp_buffer;
1129     int i;
1130
1131     while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1132     {
1133         p++;
1134     }
1135
1136     i = 0;
1137     for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1138     {
1139         word[i] = *p;
1140     }
1141
1142     word[__MIN( i, i_word_max -1 )] = '\0';
1143
1144     *pp_buffer = p;
1145
1146 }
1147 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1148 {
1149     char *p;
1150
1151     for( p = *pp_buffer; p < p_end; p++ )
1152     {
1153         if( p + 1 < p_end && *p == '\n' )
1154         {
1155             *pp_buffer = p + 1;
1156             return VLC_SUCCESS;
1157         }
1158         if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1159         {
1160             *pp_buffer = p + 2;
1161             return VLC_SUCCESS;
1162         }
1163     }
1164     *pp_buffer = p_end;
1165     return VLC_EGENERIC;
1166 }
1167
1168 //char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1169 static void b64_decode( char *dest, char *src )
1170 {
1171     int  i_level;
1172     int  last = 0;
1173     int  b64[256] = {
1174         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
1175         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
1176         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
1177         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
1178         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
1179         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
1180         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
1181         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
1182         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
1183         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
1184         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
1185         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
1186         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
1187         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
1188         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
1189         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
1190         };
1191
1192     for( i_level = 0; *src != '\0' > 0; src++ )
1193     {
1194         int  c;
1195
1196         c = b64[(unsigned int)*src];
1197         if( c == -1 )
1198         {
1199             src++;
1200             continue;
1201         }
1202
1203         switch( i_level )
1204         {
1205             case 0:
1206                 i_level++;
1207                 break;
1208             case 1:
1209                 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1210                 i_level++;
1211                 break;
1212             case 2:
1213                 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1214                 i_level++;
1215                 break;
1216             case 3:
1217                 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1218                 i_level = 0;
1219         }
1220         last = c;
1221     }
1222
1223     *dest = '\0';
1224 }
1225
1226 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1227 {
1228     char *psz_status;
1229     char *p, *p_end;
1230
1231     int  i;
1232     char command[32];
1233     char url[1024];
1234     char version[32];
1235     char user[512] = "";
1236     char password[512] = "";
1237
1238     //msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1239
1240     p = p_con->p_buffer;
1241     p_end = p + strlen( p ) + 1;
1242
1243     httpd_RequestGetWord( command, 32, &p, p_end );
1244     httpd_RequestGetWord( url, 1024, &p, p_end );
1245     httpd_RequestGetWord( version, 32, &p, p_end );
1246     //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1247
1248     if( strcmp( command, "GET" ) )
1249     {
1250         /* unimplemented */
1251         p_con->psz_file = strdup( "/501.html" );
1252         p_con->i_http_error = 501;
1253         goto create_header;
1254     }
1255
1256     if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1257     {
1258         p_con->psz_file = strdup( "/505.html" );
1259         p_con->i_http_error = 505;
1260
1261         goto create_header;
1262     }
1263
1264     /* parse headers */
1265     for( ;; )
1266     {
1267         char header[1024];
1268
1269         if( httpd_RequestNextLine( &p, p_end ) )
1270         {
1271             //msg_Dbg( p_httpt, "failled new line" );
1272             break;;
1273         }
1274         //msg_Dbg( p_httpt, "new line=%s", p );
1275
1276         httpd_RequestGetWord( header, 1024, &p, p_end );
1277
1278         if( !strcmp( header, "Authorization:" ) )
1279         {
1280             char method[32];
1281
1282             httpd_RequestGetWord( method, 32, &p, p_end );
1283             if( !strcasecmp( method, "BASIC" ) )
1284             {
1285                 char basic[1024];
1286                 char decoded[1024];
1287
1288                 httpd_RequestGetWord( basic, 1024, &p, p_end );
1289                 //msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1290                 b64_decode( decoded, basic );
1291
1292                 //msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1293                 if( strchr( decoded, ':' ) )
1294                 {
1295                     char *p = strchr( decoded, ':' );
1296
1297                     p[0] = '\0'; p++;
1298                     strcpy( user, decoded );
1299                     strcpy( password, p );
1300                 }
1301             }
1302         }
1303     }
1304
1305     p_con->psz_file = strdup( url );
1306     p_con->i_http_error = 200;
1307
1308 create_header:
1309     //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1310     FREE( p_con->p_buffer );
1311     p_con->i_buffer = 0;
1312     p_con->i_buffer_size = 0;
1313
1314     //vlc_mutex_lock( &p_httpt->file_lock );
1315 search_file:
1316     /* search file */
1317     for( i = 0, p_con->p_file = NULL; i < p_httpt->i_file_count; i++ )
1318     {
1319         if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1320         {
1321             p_con->p_file = p_httpt->file[i];
1322         }
1323     }
1324
1325     if( !p_con->p_file )
1326     {
1327         p_con->psz_file = strdup( "/404.html" );
1328         p_con->i_http_error = 404;
1329
1330         /* XXX be sure that "/404.html" exist else ... */
1331         goto search_file;
1332     }
1333
1334     if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1335     {
1336         if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) )
1337         {
1338             p_con->psz_file = strdup( "/401.html" );
1339             strcpy( user, p_con->p_file->psz_user );
1340             p_con->i_http_error = 401;
1341
1342             /* XXX do not put password on 404 else ... */
1343             goto search_file;
1344         }
1345     }
1346
1347     p_con->p_file->i_ref++;
1348 //    vlc_mutex_unlock( &p_httpt->file_lock );
1349
1350     switch( p_con->i_http_error )
1351     {
1352         case 200:
1353             psz_status = "OK";
1354             break;
1355
1356         case 401:
1357             psz_status = "Authorization Required";
1358             break;
1359         default:
1360             psz_status = "Unknown";
1361             break;
1362     }
1363
1364     p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1365
1366     /* we send stream header with this one */
1367     if( p_con->i_http_error == 200 && p_con->p_file->b_stream )
1368     {
1369         p_con->i_buffer_size = 4096 + p_con->p_file->i_header_size;
1370     }
1371
1372     p_con->i_buffer_size = 4096;
1373     p_con->i_buffer = 0;
1374     p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1375
1376     p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1377     p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1378     if( p_con->i_http_error == 401 )
1379     {
1380         p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1381     }
1382     p += sprintf( p, "\r\n" );
1383
1384     p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1;
1385
1386     if( p_con->i_http_error == 200 && p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 )
1387     {
1388         /* add stream header */
1389         memcpy( &p_con->p_buffer[p_con->i_buffer_size],
1390                 p_con->p_file->p_header,
1391                 p_con->p_file->i_header_size );
1392         p_con->i_buffer_size += p_con->p_file->i_header_size;
1393     }
1394
1395     //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1396 }
1397 #define HTTPD_STREAM_PACKET 10000
1398 static void httpd_Thread( httpd_sys_t *p_httpt )
1399 {
1400     httpd_file_t    *p_page_admin;
1401     httpd_file_t    *p_page_401;
1402     httpd_file_t    *p_page_404;
1403
1404     httpd_connection_t *p_con;
1405     vlc_bool_t         b_wait;
1406
1407     msg_Info( p_httpt, "httpd started" );
1408
1409     p_page_401 = _RegisterFile( p_httpt,
1410                                 "/401.html", "text/html",
1411                                 NULL, NULL,
1412                                 httpd_page_401_fill,
1413                                 (httpd_file_callback_args_t*)NULL );
1414     p_page_404 = _RegisterFile( p_httpt,
1415                                 "/404.html", "text/html",
1416                                 NULL, NULL,
1417                                 httpd_page_404_fill,
1418                                 (httpd_file_callback_args_t*)NULL );
1419     p_page_admin = _RegisterFile( p_httpt,
1420                                   "/admin.html", "text/html",
1421                                   "admin", "salut",
1422                                   httpd_page_admin_fill,
1423                                   (httpd_file_callback_args_t*)p_httpt );
1424
1425     while( !p_httpt->b_die )
1426     {
1427         int i;
1428         if( p_httpt->i_host_count <= 0 )
1429         {
1430             msleep( 100 * 1000 );
1431             continue;
1432         }
1433         vlc_mutex_lock( &p_httpt->host_lock );
1434         /* accept/refuse new connection */
1435         for( i = 0; i < p_httpt->i_host_count; i++ )
1436         {
1437             int     i_sock_size = sizeof( struct sockaddr_in );
1438             struct  sockaddr_in sock;
1439             int     fd;
1440
1441             fd = accept( p_httpt->host[i]->fd, (struct sockaddr *)&sock,
1442                          &i_sock_size );
1443             if( fd > 0 )
1444             {
1445 #if defined( WIN32 ) || defined( UNDER_CE )
1446                 {
1447                     unsigned long i_dummy = 1;
1448                     ioctlsocket( fd, FIONBIO, &i_dummy );
1449                 }
1450 #else
1451                 fcntl( fd, F_SETFL, O_NONBLOCK );
1452 #endif
1453
1454                 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
1455                 {
1456                     msg_Warn( p_httpt, "max connection reached" );
1457                     SOCKET_CLOSE( fd );
1458                     continue;
1459                 }
1460                 /* create a new connection and link it */
1461                 httpd_ConnnectionNew( p_httpt, fd, &sock );
1462
1463             }
1464         }
1465         vlc_mutex_unlock( &p_httpt->host_lock );
1466
1467         vlc_mutex_lock( &p_httpt->file_lock );
1468         /* now do work for all connections */
1469         for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1470         {
1471             if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1472             {
1473                 int i_len;
1474                 /* read data */
1475                 i_len = recv( p_con->fd,
1476                               p_con->p_buffer + p_con->i_buffer,
1477                               p_con->i_buffer_size - p_con->i_buffer, 0 );
1478
1479                 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
1480                     ( i_len == 0 ) )
1481                 {
1482                     httpd_connection_t *p_next = p_con->p_next;
1483
1484                     httpd_ConnnectionClose( p_httpt, p_con );
1485                     p_con = p_next;
1486                 }
1487                 else if( i_len > 0 )
1488                 {
1489                     uint8_t *ptr;
1490
1491                     p_con->i_buffer += i_len;
1492
1493                     ptr = p_con->p_buffer + p_con->i_buffer;
1494
1495                     if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
1496                         ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
1497                         p_con->i_buffer >= p_con->i_buffer_size )
1498                     {
1499                         p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
1500                         httpd_ConnectionParseRequest( p_httpt, p_con );
1501                     }
1502
1503                     p_con = p_con->p_next;
1504                 }
1505                 else
1506                 {
1507                     p_con = p_con->p_next;
1508                 }
1509                 continue;   /* just for clarity */
1510             }
1511             else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
1512             {
1513                 int i_len;
1514
1515                 /* write data */
1516                 i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
1517
1518 //                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 );
1519
1520                 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR )||
1521                     ( i_len == 0 ) )
1522                 {
1523                     httpd_connection_t *p_next = p_con->p_next;
1524
1525                     httpd_ConnnectionClose( p_httpt, p_con );
1526                     p_con = p_next;
1527                 }
1528                 else if( i_len > 0 )
1529                 {
1530                     p_con->i_buffer += i_len;
1531
1532                     if( p_con->i_buffer >= p_con->i_buffer_size )
1533                     {
1534                         if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
1535                         {
1536                             p_con->i_buffer_size = 0;
1537                             p_con->i_buffer = 0;
1538                             FREE( p_con->p_buffer );
1539
1540                             if( !p_con->p_file->b_stream )
1541                             {
1542                                 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
1543                                 p_con->p_file->pf_fill( p_con->p_file->p_sys, &p_con->p_buffer, &p_con->i_buffer_size );
1544                             }
1545                             else
1546                             {
1547                                 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
1548                                 p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos;
1549                             }
1550                             p_con = p_con->p_next;
1551                         }
1552                         else
1553                         {
1554                             httpd_connection_t *p_next = p_con->p_next;
1555
1556                             httpd_ConnnectionClose( p_httpt, p_con );
1557                             p_con = p_next;
1558                         }
1559                     }
1560                     else
1561                     {
1562                         p_con = p_con->p_next;
1563                     }
1564                 }
1565                 else
1566                 {
1567                     p_con = p_con->p_next;
1568                 }
1569                 continue;   /* just for clarity */
1570             }
1571             else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
1572             {
1573                 httpd_stream_t *p_stream = p_con->p_file;
1574                 int i_send;
1575                 int i_write;
1576
1577                 if( p_con->i_stream_pos < p_stream->i_buffer_pos )
1578                 {
1579                     int i_pos;
1580                     /* check if this p_con aren't to late */
1581                     if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos )
1582                     {
1583                         fprintf(stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n", p_con->i_stream_pos, p_stream->i_buffer_pos  );
1584                         p_con->i_stream_pos = p_stream->i_buffer_last_pos;
1585                     }
1586
1587                     i_pos = p_con->i_stream_pos % p_stream->i_buffer_size;
1588                     /* size until end of buffer */
1589                     i_write = p_stream->i_buffer_size - i_pos;
1590                     /* is it more than valid data */
1591                     if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos )
1592                     {
1593                         i_write = p_stream->i_buffer_pos - p_con->i_stream_pos;
1594                     }
1595                     /* limit to HTTPD_STREAM_PACKET */
1596                     if( i_write > HTTPD_STREAM_PACKET )
1597                     {
1598                         i_write = HTTPD_STREAM_PACKET;
1599                     }
1600                     i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 );
1601
1602                     if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )|| ( i_send == 0 ) )
1603                     {
1604                         httpd_connection_t *p_next = p_con->p_next;
1605
1606                         httpd_ConnnectionClose( p_httpt, p_con );
1607                         p_con = p_next;
1608                         continue;
1609                     }
1610                     else if( i_send > 0 )
1611                     {
1612                         p_con->i_stream_pos += i_send;
1613                     }
1614                 }
1615                 p_con = p_con->p_next;
1616                 continue;   /* just for clarity */
1617             }
1618             else
1619             {
1620                 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
1621                 p_con = p_con->p_next;
1622             }
1623         }   /* for over connection */
1624
1625 #if 0
1626         b_wait = VLC_TRUE;
1627         /* update position for stream based file */
1628         for( i = 0; i < p_httpt->i_file_count; i++ )
1629         {
1630             if( p_httpt->file[i]->b_stream )
1631             {
1632                 p_httpt->file[i]->i_buffer += __MIN( p_httpt->file[i]->i_buffer_valid - p_httpt->file[i]->i_buffer,
1633                                                      HTTPD_STREAM_PACKET );
1634                 if( p_httpt->file[i]->i_buffer < p_httpt->file[i]->i_buffer_valid )
1635                 {
1636                     /* there is data */
1637                     b_wait = VLC_FALSE;
1638                 }
1639             }
1640         }
1641 #endif
1642         vlc_mutex_unlock( &p_httpt->file_lock );
1643         /*if( b_wait )*/ msleep( 10 );
1644     }
1645     msg_Info( p_httpt, "httpd stopped" );
1646
1647     _UnregisterFile( p_httpt, p_page_401 );
1648     _UnregisterFile( p_httpt, p_page_404 );
1649     _UnregisterFile( p_httpt, p_page_admin );
1650 }