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