* thanks to Cif, added "Ban", "Unban", "Kick/Ban" options in http administration...
[vlc.git] / modules / misc / httpd.c
1 /*****************************************************************************
2  * httpd.c
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: httpd.c,v 1.17 2003/07/01 08:30:49 adn 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/types.h>
30 #include <sys/stat.h>
31
32 #include <string.h>
33 #include <errno.h>
34 #include <fcntl.h>
35
36 #include <vlc/vlc.h>
37 #include "httpd.h"
38
39 #ifdef HAVE_SYS_TIME_H
40 #    include <sys/time.h>
41 #endif
42
43 #ifdef HAVE_UNISTD_H
44 #   include <unistd.h>
45 #endif
46
47 #if defined( UNDER_CE )
48 #   include <winsock.h>
49 #elif defined( WIN32 )
50 #   include <winsock2.h>
51 #   include <ws2tcpip.h>
52 #   ifndef IN_MULTICAST
53 #       define IN_MULTICAST(a) IN_CLASSD(a)
54 #   endif
55 #else
56 #   include <netdb.h>                                         /* hostent ... */
57 #   include <sys/socket.h>
58 #   include <netinet/in.h>
59 #   ifdef HAVE_ARPA_INET_H
60 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
61 #   endif
62 #endif
63
64 #include "network.h"
65
66 #ifndef INADDR_ANY
67 #   define INADDR_ANY  0x00000000
68 #endif
69 #ifndef INADDR_NONE
70 #   define INADDR_NONE 0xFFFFFFFF
71 #endif
72
73 #define LISTEN_BACKLOG          100
74 #define HTTPD_MAX_CONNECTION    512
75 #define HTTPD_CONNECTION_MAX_UNUSED 10000000
76
77 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
78
79 #if defined( WIN32 ) || defined( UNDER_CE )
80 #define SOCKET_CLOSE(a)    closesocket(a)
81 #else
82 #define SOCKET_CLOSE(a)    close(a)
83 #endif
84
85 /*****************************************************************************
86  * Exported prototypes
87  *****************************************************************************/
88 static int              Open   ( vlc_object_t * );
89 static void             Close  ( vlc_object_t * );
90
91 /*****************************************************************************
92  * Module descriptor
93  *****************************************************************************/
94 vlc_module_begin();
95     set_description( _("HTTP 1.0 daemon") );
96     set_capability( "httpd", 42 );
97     set_callbacks( Open, Close );
98 vlc_module_end();
99
100 /*****************************************************************************
101  * Prototypes
102  *****************************************************************************/
103 static httpd_host_t     *RegisterHost   ( httpd_t *, char *, int );
104 static void             UnregisterHost  ( httpd_t *, httpd_host_t * );
105
106 static httpd_file_t     *RegisterFile   ( httpd_t *,
107                                           char *psz_file, char *psz_mime,
108                                           char *psz_user, char *psz_password,
109                                           httpd_file_callback pf_get,
110                                           httpd_file_callback pf_post,
111                                           httpd_file_callback_args_t *p_args );
112 static void             UnregisterFile  ( httpd_t *, httpd_file_t * );
113
114 //#define httpd_stream_t              httpd_file_t
115 static httpd_stream_t   *RegisterStream ( httpd_t *,
116                                          char *psz_file, char *psz_mime,
117                                          char *psz_user, char *psz_password );
118 static int              SendStream      ( httpd_t *, httpd_stream_t *, uint8_t *, int );
119 static int              HeaderStream    ( httpd_t *, httpd_stream_t *, uint8_t *, int );
120 static void             UnregisterStream( httpd_t *, httpd_stream_t* );
121
122 /*****************************************************************************
123  * Internal definitions
124  *****************************************************************************/
125 struct httpd_host_t
126 {
127     int    i_ref;
128
129     char   *psz_host_addr;
130     int    i_port;
131
132     struct sockaddr_in sock;
133     int    fd;
134
135 };
136
137 #define HTTPD_AUTHENTICATE_NONE     0
138 #define HTTPD_AUTHENTICATE_BASIC    1
139
140 //typedef httpd_file_t httpd_stream_t;
141
142 struct httpd_file_t
143 {
144     int         i_ref;
145
146     char        *psz_file;
147     char        *psz_mime;
148
149
150     int         i_authenticate_method;
151         char        *psz_user;          /* NULL if no auth */
152         char        *psz_password;      /* NULL if no auth */
153
154     vlc_bool_t  b_stream;               /* if false: httpd will retreive data by a callback
155                                               true:  it's up to the program to give data to httpd */
156     void                *p_sys;         /* provided for user */
157     httpd_file_callback pf_get;         /* it should allocate and fill *pp_data and *pi_data */
158     httpd_file_callback pf_post;        /* it should allocate and fill *pp_data and *pi_data */
159
160     /* private */
161
162     /* circular buffer for stream only */
163     int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
164     uint8_t     *p_buffer;          /* buffer */
165     int64_t     i_buffer_pos;       /* absolute position from begining */
166     int         i_buffer_last_pos;  /* a new connection will start with that */
167
168     /* data to be send at connection time (if any) */
169     int         i_header_size;
170     uint8_t     *p_header;
171 };
172
173
174 #define HTTPD_CONNECTION_RECEIVING_REQUEST      1
175 #define HTTPD_CONNECTION_SENDING_HEADER         2
176 #define HTTPD_CONNECTION_SENDING_FILE           3
177 #define HTTPD_CONNECTION_SENDING_STREAM         4
178 #define HTTPD_CONNECTION_TO_BE_CLOSED           5
179
180 #define HTTPD_CONNECTION_METHOD_GET             1
181 #define HTTPD_CONNECTION_METHOD_POST            2
182 typedef struct httpd_connection_s
183 {
184     struct httpd_connection_s *p_next;
185     struct httpd_connection_s *p_prev;
186
187     struct  sockaddr_in sock;
188     int     fd;
189     mtime_t i_last_activity_date;
190
191     int    i_state;
192     int    i_method;       /* get/post */
193
194     char    *psz_file;      // file to be send
195     int     i_http_error;   // error to be send with the file
196     char    *psz_user;      // if Authorization in the request header
197     char    *psz_password;
198
199     uint8_t *p_request;     // whith get: ?<*>, with post: main data
200     int      i_request_size;
201
202     httpd_file_t    *p_file;
203
204     /* used while sending header and file */
205     int     i_buffer_size;
206     uint8_t *p_buffer;
207     int     i_buffer;            /* private */
208
209     /* used for stream */
210     int64_t i_stream_pos;   /* absolute pos in stream */
211 } httpd_connection_t;
212
213 /* Linked List of banned IP */
214 typedef struct httpd_banned_ip_s
215 {
216     struct httpd_banned_ip_s *p_next;
217     struct httpd_banned_ip_s *p_prev;
218     
219     char *psz_ip;
220
221 } httpd_banned_ip_t;
222 /*
223  * The httpd thread
224  */
225 struct httpd_sys_t
226 {
227     VLC_COMMON_MEMBERS
228
229     vlc_mutex_t             host_lock;
230     volatile int            i_host_count;
231     httpd_host_t            **host;
232
233     vlc_mutex_t             file_lock;
234     int                     i_file_count;
235     httpd_file_t            **file;
236
237     vlc_mutex_t             connection_lock;
238     int                     i_connection_count;
239     httpd_connection_t      *p_first_connection;
240
241     vlc_mutex_t             ban_lock;
242     int                     i_banned_ip_count;
243     httpd_banned_ip_t       *p_first_banned_ip;           
244 };
245
246 static void httpd_Thread( httpd_sys_t *p_httpt );
247 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
248 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
249 static int httpd_UnbanIP( httpd_sys_t *, httpd_banned_ip_t *);
250 static int httpd_BanIP( httpd_sys_t *, char *);
251 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *, char * );
252 /*****************************************************************************
253  * Open:
254  *****************************************************************************/
255
256 static int Open( vlc_object_t *p_this )
257 {
258     httpd_t     *p_httpd = (httpd_t*)p_this;
259     httpd_sys_t *p_httpt;
260
261     /* Launch httpt thread */
262     if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
263     {
264         msg_Err( p_this, "out of memory" );
265         return( VLC_EGENERIC );
266     }
267
268     p_httpt->b_die  = 0;
269     p_httpt->b_error= 0;
270
271     /* init httpt_t structure */
272     vlc_mutex_init( p_httpd, &p_httpt->host_lock );
273     p_httpt->i_host_count = 0;
274     p_httpt->host = NULL;
275
276     vlc_mutex_init( p_httpd, &p_httpt->file_lock );
277     p_httpt->i_file_count = 0;
278     p_httpt->file = NULL;
279
280     vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
281     p_httpt->i_connection_count = 0;
282     p_httpt->p_first_connection = NULL;
283
284     vlc_mutex_init( p_httpd, &p_httpt->ban_lock );
285     p_httpt->i_banned_ip_count = 0;
286     p_httpt->p_first_banned_ip = NULL;
287                 
288     /* start the thread */
289     if( vlc_thread_create( p_httpt, "httpd thread",
290                            httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
291     {
292         msg_Err( p_this, "cannot spawn http thread" );
293
294         vlc_mutex_destroy( &p_httpt->host_lock );
295         vlc_mutex_destroy( &p_httpt->file_lock );
296         vlc_mutex_destroy( &p_httpt->connection_lock );
297         vlc_mutex_destroy( &p_httpt->ban_lock );                
298
299         vlc_object_destroy( p_httpt );
300         return( VLC_EGENERIC );
301     }
302
303     msg_Info( p_httpd, "http thread launched" );
304
305     p_httpd->p_sys = p_httpt;
306     p_httpd->pf_register_host   = RegisterHost;
307     p_httpd->pf_unregister_host = UnregisterHost;
308     p_httpd->pf_register_file   = RegisterFile;
309     p_httpd->pf_unregister_file = UnregisterFile;
310     p_httpd->pf_register_stream = RegisterStream;
311     p_httpd->pf_header_stream   = HeaderStream;
312     p_httpd->pf_send_stream     = SendStream;
313     p_httpd->pf_unregister_stream=UnregisterStream;
314
315     return( VLC_SUCCESS );
316 }
317
318 /*****************************************************************************
319  * Close: close the target
320  *****************************************************************************/
321 static void Close( vlc_object_t * p_this )
322 {
323     httpd_t     *p_httpd = (httpd_t*)p_this;
324     httpd_sys_t *p_httpt = p_httpd->p_sys;
325
326     httpd_connection_t *p_con;
327     httpd_banned_ip_t *p_banned_ip;
328     
329     int i;
330
331     p_httpt->b_die = 1;
332     vlc_thread_join( p_httpt );
333
334     /* first close all host */
335     vlc_mutex_destroy( &p_httpt->host_lock );
336     if( p_httpt->i_host_count )
337     {
338         msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
339     }
340     for( i = 0; i < p_httpt->i_host_count; i++ )
341     {
342 #define p_host p_httpt->host[i]
343         FREE( p_host->psz_host_addr );
344         SOCKET_CLOSE( p_host->fd );
345
346         FREE( p_host );
347 #undef p_host
348     }
349     FREE( p_httpt->host );
350
351     /* now all file */
352     vlc_mutex_destroy( &p_httpt->file_lock );
353     if( p_httpt->i_file_count )
354     {
355         msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
356     }
357     for( i = 0; i < p_httpt->i_file_count; i++ )
358     {
359 #define p_file p_httpt->file[i]
360         FREE( p_file->psz_file );
361         FREE( p_file->psz_mime );
362         if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
363         {
364             FREE( p_file->psz_user );
365             FREE( p_file->psz_password );
366         }
367         FREE( p_file->p_buffer );
368
369         FREE( p_file );
370 #undef p_file
371     }
372     FREE( p_httpt->file );
373
374     /* andd close all connection */
375     vlc_mutex_destroy( &p_httpt->connection_lock );
376     if( p_httpt->i_connection_count )
377     {
378         msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
379     }
380     while( ( p_con = p_httpt->p_first_connection ) )
381     {
382         httpd_ConnnectionClose( p_httpt, p_con );
383     }
384     
385     /* Free all banned IP */   
386     vlc_mutex_destroy( &p_httpt->ban_lock );
387     while( ( p_banned_ip = p_httpt->p_first_banned_ip))
388     {
389         httpd_UnbanIP(p_httpt,p_banned_ip);
390     }
391             
392     msg_Info( p_httpd, "httpd instance closed" );
393     vlc_object_destroy( p_httpt );
394 }
395
396
397 /****************************************************************************
398  ****************************************************************************
399  ***
400  ***
401  ****************************************************************************
402  ****************************************************************************/
403 static int BuildAddr( struct sockaddr_in * p_socket,
404                       const char * psz_address, int i_port )
405 {
406     /* Reset struct */
407     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
408     p_socket->sin_family = AF_INET;                                /* family */
409     p_socket->sin_port = htons( (uint16_t)i_port );
410     if( !*psz_address )
411     {
412         p_socket->sin_addr.s_addr = INADDR_ANY;
413     }
414     else
415     {
416         struct hostent    * p_hostent;
417
418         /* Try to convert address directly from in_addr - this will work if
419          * psz_address is dotted decimal. */
420 #ifdef HAVE_ARPA_INET_H
421         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
422 #else
423         p_socket->sin_addr.s_addr = inet_addr( psz_address );
424         if( p_socket->sin_addr.s_addr == INADDR_NONE )
425 #endif
426         {
427             /* We have a fqdn, try to find its address */
428             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
429             {
430                 return( -1 );
431             }
432
433             /* Copy the first address of the host in the socket address */
434             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
435                      p_hostent->h_length );
436         }
437     }
438     return( 0 );
439 }
440
441
442 /*
443  * listen on a host for a httpd instance
444  */
445
446 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
447 {
448     httpd_host_t    *p_host;
449     struct sockaddr_in  sock;
450     int i;
451     int fd = -1;
452     int i_opt;
453 #if !defined( WIN32 ) && !defined( UNDER_CE )
454     int i_flags;
455 #endif
456
457     if( BuildAddr( &sock, psz_host_addr, i_port ) )
458     {
459         msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
460         return NULL;
461     }
462
463     /* is it already declared ? */
464     vlc_mutex_lock( &p_httpt->host_lock );
465     for( i = 0; i < p_httpt->i_host_count; i++ )
466     {
467         if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
468             p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr )
469         {
470             break;
471         }
472     }
473
474     if( i < p_httpt->i_host_count )
475     {
476         /* yes, increment ref count and succed */
477         p_httpt->host[i]->i_ref++;
478         vlc_mutex_unlock( &p_httpt->host_lock );
479         return( p_httpt->host[i] );
480     }
481
482     /* need to add a new listening socket */
483
484     /* open socket */
485     fd = socket( AF_INET, SOCK_STREAM, 0 );
486     if( fd < 0 )
487     {
488         msg_Err( p_httpt, "cannot open socket" );
489         goto socket_failed;
490     }
491     /* reuse socket */
492     i_opt = 1;
493     if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
494                     (void *) &i_opt, sizeof( i_opt ) ) < 0 )
495     {
496         msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
497     }
498     /* bind it */
499     if( bind( fd, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 )
500     {
501         msg_Err( p_httpt, "cannot bind socket" );
502         goto socket_failed;
503     }
504     /* set to non-blocking */
505 #if defined( WIN32 ) || defined( UNDER_CE )
506     {
507         unsigned long i_dummy = 1;
508         if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
509         {
510             msg_Err( p_httpt, "cannot set socket to non-blocking mode" );
511             goto socket_failed;
512         }
513     }
514 #else
515     if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
516     {
517         msg_Err( p_httpt, "cannot F_GETFL socket" );
518         goto socket_failed;
519     }
520     if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
521     {
522         msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
523         goto socket_failed;
524     }
525 #endif
526     /* listen */
527     if( listen( fd, LISTEN_BACKLOG ) < 0 )
528     {
529         msg_Err( p_httpt, "cannot listen socket" );
530         goto socket_failed;
531     }
532
533     if( p_httpt->host )
534     {
535         p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *)  * ( p_httpt->i_host_count + 1 ) );
536     }
537     else
538     {
539         p_httpt->host = malloc( sizeof( httpd_host_t *) );
540     }
541     p_host                = malloc( sizeof( httpd_host_t ) );
542     p_host->i_ref         = 1;
543     p_host->psz_host_addr = strdup( psz_host_addr );
544     p_host->i_port        = i_port;
545     p_host->sock          = sock;
546     p_host->fd            = fd;
547
548     p_httpt->host[p_httpt->i_host_count++] = p_host;
549     vlc_mutex_unlock( &p_httpt->host_lock );
550
551     return p_host;
552
553 socket_failed:
554     vlc_mutex_unlock( &p_httpt->host_lock );
555     if( fd >= 0 )
556     {
557         SOCKET_CLOSE( fd );
558     }
559     return NULL;
560 }
561 static httpd_host_t     *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
562 {
563     return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
564 }
565
566 /*
567  * remove a listening host for an httpd instance
568  */
569 static void            _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
570 {
571     int i;
572
573     vlc_mutex_lock( &p_httpt->host_lock );
574     for( i = 0; i < p_httpt->i_host_count; i++ )
575     {
576         if( p_httpt->host[i] == p_host )
577         {
578             break;
579         }
580     }
581     if( i >= p_httpt->i_host_count )
582     {
583         vlc_mutex_unlock( &p_httpt->host_lock );
584         msg_Err( p_httpt, "cannot unregister host" );
585         return;
586     }
587
588     p_host->i_ref--;
589
590     if( p_host->i_ref > 0 )
591     {
592         /* still in use */
593         vlc_mutex_unlock( &p_httpt->host_lock );
594         return;
595     }
596
597     /* no more used */
598     FREE( p_host->psz_host_addr );
599     SOCKET_CLOSE( p_host->fd );
600
601     FREE( p_host );
602
603     if( p_httpt->i_host_count <= 1 )
604     {
605         FREE( p_httpt->host );
606         p_httpt->i_host_count = 0;
607     }
608     else
609     {
610         int i_move;
611
612         i_move = p_httpt->i_host_count - i - 1;
613
614         if( i_move > 0 )
615         {
616             memmove( &p_httpt->host[i],
617                      &p_httpt->host[i+1],
618                      i_move * sizeof( httpd_host_t * ) );
619         }
620
621         p_httpt->i_host_count--;
622         p_httpt->host = realloc( p_httpt->host,
623                                  p_httpt->i_host_count * sizeof( httpd_host_t * ) );
624     }
625
626     vlc_mutex_unlock( &p_httpt->host_lock );
627 }
628 static void             UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
629 {
630     _UnregisterHost( p_httpd->p_sys, p_host );
631 }
632
633
634 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
635 {
636     /* add a new file */
637     if( p_httpt->i_file_count )
638     {
639         p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *)  * ( p_httpt->i_file_count + 1 ) );
640     }
641     else
642     {
643         p_httpt->file = malloc( sizeof( httpd_file_t *) );
644     }
645
646     p_httpt->file[p_httpt->i_file_count++] = p_file;
647 }
648
649 static httpd_file_t    *_RegisterFile( httpd_sys_t *p_httpt,
650                                        char *psz_file, char *psz_mime,
651                                        char *psz_user, char *psz_password,
652                                        httpd_file_callback pf_get,
653                                        httpd_file_callback pf_post,
654                                        httpd_file_callback_args_t *p_args )
655 {
656     httpd_file_t    *p_file;
657     int i;
658
659     vlc_mutex_lock( &p_httpt->file_lock );
660     for( i = 0; i < p_httpt->i_file_count; i++ )
661     {
662         if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
663         {
664             break;
665         }
666     }
667     if( i < p_httpt->i_file_count )
668     {
669         vlc_mutex_unlock( &p_httpt->file_lock );
670         msg_Err( p_httpt, "%s already registered", psz_file );
671         return NULL;
672     }
673
674     p_file              = malloc( sizeof( httpd_file_t ) );
675     p_file->i_ref       = 0;
676     p_file->psz_file    = strdup( psz_file );
677     p_file->psz_mime    = strdup( psz_mime );
678     if( psz_user && *psz_user )
679     {
680         p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
681         p_file->psz_user              = strdup( psz_user );
682         p_file->psz_password          = strdup( psz_password );
683     }
684     else
685     {
686         p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
687         p_file->psz_user              = NULL;
688         p_file->psz_password          = NULL;
689     }
690
691     p_file->b_stream          = VLC_FALSE;
692     p_file->p_sys             = p_args;
693     p_file->pf_get            = pf_get;
694     p_file->pf_post           = pf_post;
695
696     p_file->i_buffer_size     = 0;
697     p_file->i_buffer_last_pos = 0;
698     p_file->i_buffer_pos      = 0;
699     p_file->p_buffer          = NULL;
700
701     p_file->i_header_size     = 0;
702     p_file->p_header          = NULL;
703
704     __RegisterFile( p_httpt, p_file );
705
706     vlc_mutex_unlock( &p_httpt->file_lock );
707
708     return p_file;
709 }
710 static httpd_file_t     *RegisterFile( httpd_t *p_httpd,
711                                        char *psz_file, char *psz_mime,
712                                        char *psz_user, char *psz_password,
713                                        httpd_file_callback pf_get,
714                                        httpd_file_callback pf_post,
715                                        httpd_file_callback_args_t *p_args )
716 {
717     return( _RegisterFile( p_httpd->p_sys,
718                            psz_file, psz_mime, psz_user, psz_password,
719                            pf_get, pf_post, p_args ) );
720 }
721
722 static httpd_stream_t  *_RegisterStream( httpd_sys_t *p_httpt,
723                                          char *psz_file, char *psz_mime,
724                                          char *psz_user, char *psz_password )
725 {
726     httpd_stream_t    *p_stream;
727     int i;
728
729     vlc_mutex_lock( &p_httpt->file_lock );
730     for( i = 0; i < p_httpt->i_file_count; i++ )
731     {
732         if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
733         {
734             break;
735         }
736     }
737     if( i < p_httpt->i_file_count )
738     {
739         vlc_mutex_unlock( &p_httpt->file_lock );
740         msg_Err( p_httpt, "%s already registered", psz_file );
741         return NULL;
742     }
743
744     p_stream              = malloc( sizeof( httpd_stream_t ) );
745     p_stream->i_ref       = 0;
746     p_stream->psz_file    = strdup( psz_file );
747     p_stream->psz_mime    = strdup( psz_mime );
748     if( psz_user && *psz_user )
749     {
750         p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
751         p_stream->psz_user              = strdup( psz_user );
752         p_stream->psz_password          = strdup( psz_password );
753     }
754     else
755     {
756         p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
757         p_stream->psz_user              = NULL;
758         p_stream->psz_password          = NULL;
759     }
760
761     p_stream->b_stream        = VLC_TRUE;
762     p_stream->p_sys           = NULL;
763     p_stream->pf_get          = NULL;
764     p_stream->pf_post         = NULL;
765
766     p_stream->i_buffer_size   = 5*1024*1024;
767     p_stream->i_buffer_pos      = 0;
768     p_stream->i_buffer_last_pos = 0;
769     p_stream->p_buffer        = malloc( p_stream->i_buffer_size );
770
771     p_stream->i_header_size   = 0;
772     p_stream->p_header        = NULL;
773
774     __RegisterFile( p_httpt, p_stream );
775
776     vlc_mutex_unlock( &p_httpt->file_lock );
777
778     return p_stream;
779 }
780 static httpd_stream_t   *RegisterStream( httpd_t *p_httpd,
781                                          char *psz_file, char *psz_mime,
782                                          char *psz_user, char *psz_password )
783 {
784     return( _RegisterStream( p_httpd->p_sys,
785                              psz_file, psz_mime, psz_user, psz_password ) );
786 }
787
788 static void            _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
789 {
790     int i;
791
792     vlc_mutex_lock( &p_httpt->file_lock );
793     for( i = 0; i < p_httpt->i_file_count; i++ )
794     {
795         if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
796         {
797             break;
798         }
799     }
800     if( i >= p_httpt->i_file_count )
801     {
802         vlc_mutex_unlock( &p_httpt->file_lock );
803         msg_Err( p_httpt, "cannot unregister file" );
804         return;
805     }
806
807     if( p_file->i_ref > 0 )
808     {
809         httpd_connection_t *p_con;
810         /* force closing all connection for this file */
811         msg_Err( p_httpt, "closing all client connection" );
812
813         vlc_mutex_lock( &p_httpt->connection_lock );
814         for( p_con = p_httpt->p_first_connection; p_con != NULL; )
815         {
816             httpd_connection_t *p_next;
817
818             p_next = p_con->p_next;
819             if( p_con->p_file == p_file )
820             {
821                 httpd_ConnnectionClose( p_httpt, p_con );
822             }
823             p_con = p_next;
824         }
825         vlc_mutex_unlock( &p_httpt->connection_lock );
826     }
827
828     FREE( p_file->psz_file );
829     FREE( p_file->psz_mime );
830     if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
831     {
832         FREE( p_file->psz_user );
833         FREE( p_file->psz_password );
834     }
835     FREE( p_file->p_buffer );
836     FREE( p_file->p_header );
837
838     FREE( p_file );
839
840
841     if( p_httpt->i_file_count == 1 )
842     {
843         FREE( p_httpt->file );
844         p_httpt->i_file_count = 0;
845     }
846     else
847     {
848         int i_move;
849
850         i_move = p_httpt->i_file_count - i - 1;
851         if( i_move > 0  )
852         {
853             memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
854         }
855         p_httpt->i_file_count--;
856         p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
857     }
858
859     vlc_mutex_unlock( &p_httpt->file_lock );
860 }
861 static void            UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
862 {
863     _UnregisterFile( p_httpd->p_sys, p_file );
864 }
865
866 static void             UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
867 {
868     _UnregisterFile( p_httpd->p_sys, p_stream );
869 }
870
871
872
873 static int             _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
874 {
875     int i_count;
876     int i_pos;
877
878     if( i_data <= 0 || p_data == NULL )
879     {
880         return( VLC_SUCCESS );
881     }
882     //fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos );
883
884     vlc_mutex_lock( &p_httpt->file_lock );
885
886     /* save this pointer (to be used by new connection) */
887     p_stream->i_buffer_last_pos = p_stream->i_buffer_pos;
888
889     i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size;
890     i_count = i_data;
891     while( i_count > 0)
892     {
893         int i_copy;
894
895         i_copy = __MIN( i_count, p_stream->i_buffer_size - i_pos );
896
897         memcpy( &p_stream->p_buffer[i_pos],
898                 p_data,
899                 i_copy );
900
901         i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size;
902         i_count -= i_copy;
903         p_data += i_copy;
904     }
905
906     p_stream->i_buffer_pos += i_data;
907     vlc_mutex_unlock( &p_httpt->file_lock );
908
909     return( VLC_SUCCESS );
910 }
911 static int             SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
912 {
913     return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
914 }
915
916 static int             HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
917 {
918     httpd_sys_t *p_httpt = p_httpd->p_sys;
919
920     vlc_mutex_lock( &p_httpt->file_lock );
921
922     FREE( p_stream->p_header );
923     if( p_data == NULL || i_data <= 0 )
924     {
925         p_stream->i_header_size = 0;
926     }
927     else
928     {
929         p_stream->i_header_size = i_data;
930         p_stream->p_header = malloc( i_data );
931         memcpy( p_stream->p_header,
932                 p_data,
933                 i_data );
934     }
935     vlc_mutex_unlock( &p_httpt->file_lock );
936
937     return( VLC_SUCCESS );
938 }
939
940 /****************************************************************************/
941 /****************************************************************************/
942 /****************************************************************************/
943 /****************************************************************************/
944 /****************************************************************************/
945
946 static int  httpd_page_401_get( httpd_file_callback_args_t *p_args,
947                                 uint8_t *p_request, int i_request,
948                                 uint8_t **pp_data, int *pi_data )
949 {
950     char *p;
951
952     p = *pp_data = malloc( 1024 );
953
954     p += sprintf( p, "<html>\n" );
955     p += sprintf( p, "<head>\n" );
956     p += sprintf( p, "<title>Error 401</title>\n" );
957     p += sprintf( p, "</head>\n" );
958     p += sprintf( p, "<body>\n" );
959     p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
960     p += sprintf( p, "<hr />\n" );
961     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
962     p += sprintf( p, "</body>\n" );
963     p += sprintf( p, "</html>\n" );
964
965     *pi_data = strlen( *pp_data ) + 1;
966
967     return VLC_SUCCESS;
968 }
969 static int  httpd_page_404_get( httpd_file_callback_args_t *p_args,
970                                 uint8_t *p_request, int i_request,
971                                 uint8_t **pp_data, int *pi_data )
972 {
973     char *p;
974
975     p = *pp_data = malloc( 1024 );
976
977     p += sprintf( p, "<html>\n" );
978     p += sprintf( p, "<head>\n" );
979     p += sprintf( p, "<title>Error 404</title>\n" );
980     p += sprintf( p, "</head>\n" );
981     p += sprintf( p, "<body>\n" );
982     p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
983     p += sprintf( p, "<hr />\n" );
984     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
985     p += sprintf( p, "</body>\n" );
986     p += sprintf( p, "</html>\n" );
987
988     *pi_data = strlen( *pp_data ) + 1;
989
990     return VLC_SUCCESS;
991 }
992
993
994 static int _httpd_page_admin_get_status( httpd_file_callback_args_t *p_args,
995                                          uint8_t **pp_data, int *pi_data )
996 {
997     httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
998     httpd_connection_t *p_con;
999     httpd_banned_ip_t *p_ip;
1000         
1001     int i;
1002     char *p;
1003
1004     /* FIXME FIXME do not use static size FIXME FIXME*/
1005     p = *pp_data = malloc( 8096 );
1006
1007     p += sprintf( p, "<html>\n" );
1008     p += sprintf( p, "<head>\n" );
1009     p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
1010     p += sprintf( p, "</head>\n" );
1011     p += sprintf( p, "<body>\n" );
1012     p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
1013     p += sprintf( p, "<h2><center>Admin page</center></h2>\n" );
1014
1015     /* general */
1016     p += sprintf( p, "<h3>General state</h3>\n" );
1017     p += sprintf( p, "<ul>\n" );
1018     p += sprintf( p, "<li>Connection count: %d</li>\n", p_httpt->i_connection_count );
1019     //p += sprintf( p, "<li>Total bandwith: %d</li>\n", -1 );
1020     /*p += sprintf( p, "<li></li>\n" );*/
1021     p += sprintf( p, "<li>Ban count: %d</li>\n", p_httpt->i_banned_ip_count );
1022     p += sprintf( p, "</ul>\n" );
1023     
1024     /* ban list */
1025     /* XXX do not lock on ban_lock */
1026     p += sprintf( p, "<h3>Ban list</h3>\n" );
1027     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1028     p += sprintf( p, "<tr>\n<th>IP</th>\n<th>Action</th></tr>\n" );
1029     for( p_ip = p_httpt->p_first_banned_ip;p_ip != NULL; p_ip = p_ip->p_next )
1030     {
1031         p += sprintf( p, "<tr>\n" );
1032         p += sprintf( p, "<td>%s</td>\n", p_ip->psz_ip );
1033         p += sprintf( p, "<td><form method=\"get\" action=\"\">"
1034                          "<select name=\"action\">"
1035                          "<option selected>unban_ip</option>"
1036                          "</select>"
1037                          "<input type=\"hidden\" name=\"id\" value=\"%s\"/>"                         
1038                          "<input type=\"submit\" value=\"Do it\" />"
1039                          "</form></td>\n", p_ip->psz_ip);        
1040         p += sprintf( p, "</tr>\n" );
1041     }
1042     p += sprintf( p, "</table>\n" );
1043
1044     
1045     
1046     /* host list */
1047     vlc_mutex_lock( &p_httpt->host_lock );
1048     p += sprintf( p, "<h3>Host list</h3>\n" );
1049     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1050     p += sprintf( p, "<tr>\n<th>Host</th><th>Port</th><th>IP</th>\n</tr>\n" );
1051
1052     for( i = 0; i < p_httpt->i_host_count; i++ )
1053     {
1054         p += sprintf( p, "<tr>\n" );
1055         p += sprintf( p, "<td>%s</td>\n", p_httpt->host[i]->psz_host_addr );
1056         p += sprintf( p, "<td>%d</td>\n", p_httpt->host[i]->i_port );
1057         p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_httpt->host[i]->sock.sin_addr ) );
1058         p += sprintf( p, "</tr>\n" );
1059     }
1060     p += sprintf( p, "</table>\n" );
1061     vlc_mutex_unlock( &p_httpt->host_lock );
1062
1063     /* file list */
1064     /* XXX do not take lock on file_lock */
1065     p += sprintf( p, "<h3>File list</h3>\n" );
1066     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1067     p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
1068
1069     for( i = 0; i < p_httpt->i_file_count; i++ )
1070     {
1071         if( !p_httpt->file[i]->b_stream )
1072         {
1073             p += sprintf( p, "<tr>\n" );
1074             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
1075             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
1076             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
1077             p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
1078             p += sprintf( p, "</tr>\n" );
1079         }
1080     }
1081     p += sprintf( p, "</table>\n" );
1082
1083     /* stream list */
1084     /* XXX do not take lock on file_lock */
1085     p += sprintf( p, "<h3>Stream list</h3>\n" );
1086     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1087     p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
1088
1089     for( i = 0; i < p_httpt->i_file_count; i++ )
1090     {
1091         if( p_httpt->file[i]->b_stream )
1092         {
1093             p += sprintf( p, "<tr>\n" );
1094             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
1095             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
1096             p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
1097             p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
1098             p += sprintf( p, "</tr>\n" );
1099         }
1100     }
1101     p += sprintf( p, "</table>\n" );
1102
1103     /* connection list */
1104     /* XXX do not take lock on connection_lock */
1105     p += sprintf( p, "<h3>Connection list</h3>\n" );
1106     p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1107     p += sprintf( p, "<tr>\n<th>IP</th><th>Requested File</th><th>Status</th><th>Action</th>\n</tr>\n" );
1108
1109     for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1110     {
1111         p += sprintf( p, "<tr>\n" );
1112         p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_con->sock.sin_addr ) );
1113         p += sprintf( p, "<td>%s</td>\n", p_con->psz_file );
1114         p += sprintf( p, "<td>%d</td>\n", p_con->i_http_error );
1115         p += sprintf( p, "<td><form method=\"get\" action=\"\">"
1116                          "<select name=\"action\">"
1117                          "<option selected>close_connection</option>"
1118                          "<option>ban_ip</option>"
1119                          "<option>close_connection_and_ban_ip</option>"
1120                          "</select>"
1121                          "<input type=\"hidden\" name=\"id\" value=\"%p\"/>"
1122                          "<input type=\"submit\" value=\"Do it\" />"
1123                               "</form></td>\n", p_con);
1124         p += sprintf( p, "</tr>\n" );
1125     }
1126     p += sprintf( p, "</table>\n" );
1127
1128
1129     /* www.videolan.org */
1130     p += sprintf( p, "<hr />\n" );
1131     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1132     p += sprintf( p, "</body>\n" );
1133     p += sprintf( p, "</html>\n" );
1134
1135     *pi_data = strlen( *pp_data ) + 1;
1136
1137     return( VLC_SUCCESS );
1138 }
1139
1140 static int _httpd_page_admin_get_success( httpd_file_callback_args_t *p_args,
1141                                           uint8_t **pp_data, int *pi_data,
1142                                           char *psz_msg )
1143 {
1144     char *p;
1145
1146     p = *pp_data = malloc( 8096 );
1147
1148     p += sprintf( p, "<html>\n" );
1149     p += sprintf( p, "<head>\n" );
1150     p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
1151     p += sprintf( p, "</head>\n" );
1152     p += sprintf( p, "<body>\n" );
1153     p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
1154
1155     p += sprintf( p, "<p>Success=`%s'</p>", psz_msg );
1156     p += sprintf( p, "<a href=\"admin.html\">Retour a la page d'administration</a>\n" );
1157     
1158     p += sprintf( p, "<hr />\n" );
1159     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1160     p += sprintf( p, "</body>\n" );
1161     p += sprintf( p, "</html>\n" );
1162
1163     *pi_data = strlen( *pp_data ) + 1;
1164
1165     return( VLC_SUCCESS );
1166 }
1167
1168 static int _httpd_page_admin_get_error( httpd_file_callback_args_t *p_args,
1169                                         uint8_t **pp_data, int *pi_data,
1170                                         char *psz_error )
1171 {
1172     char *p;
1173
1174     p = *pp_data = malloc( 8096 );
1175
1176     p += sprintf( p, "<html>\n" );
1177     p += sprintf( p, "<head>\n" );
1178     p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
1179     p += sprintf( p, "</head>\n" );
1180     p += sprintf( p, "<body>\n" );
1181     p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
1182
1183     p += sprintf( p, "<p>Error=`%s'</p>", psz_error );
1184     p += sprintf( p, "<a href=\"admin.html\">Retour a la page d'administration</a>\n" );
1185
1186     p += sprintf( p, "<hr />\n" );
1187     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1188     p += sprintf( p, "</body>\n" );
1189     p += sprintf( p, "</html>\n" );
1190
1191     *pi_data = strlen( *pp_data ) + 1;
1192
1193     return( VLC_SUCCESS );
1194 }
1195
1196 static void _httpd_uri_extract_value( char *psz_uri, char *psz_name, char *psz_value, int i_value_max )
1197 {
1198     char *p;
1199
1200     p = strstr( psz_uri, psz_name );
1201     if( p )
1202     {
1203         int i_len;
1204
1205         p += strlen( psz_name );
1206         if( *p == '=' ) p++;
1207
1208         if( strchr( p, '&' ) )
1209         {
1210             i_len = strchr( p, '&' ) - p;
1211         }
1212         else
1213         {
1214             i_len = strlen( p );
1215         }
1216         i_len = __MIN( i_value_max - 1, i_len );
1217         if( i_len > 0 )
1218         {
1219             strncpy( psz_value, p, i_len );
1220             psz_value[i_len] = '\0';
1221         }
1222         else
1223         {
1224             strncpy( psz_value, "", i_value_max );
1225         }
1226     }
1227     else
1228     {
1229         strncpy( psz_value, "", i_value_max );
1230     }
1231 }
1232
1233
1234 static int  httpd_page_admin_get( httpd_file_callback_args_t *p_args,
1235                                   uint8_t *p_request, int i_request,
1236                                   uint8_t **pp_data, int *pi_data )
1237 {
1238     httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
1239     httpd_connection_t *p_con;
1240
1241     if( i_request > 0)
1242     {
1243         char action[512];
1244
1245         _httpd_uri_extract_value( p_request, "action", action, 512 );
1246
1247         if( !strcmp( action, "close_connection" ) )
1248         {
1249             char id[128];
1250             void *i_id;
1251
1252             _httpd_uri_extract_value( p_request, "id", id, 512 );
1253             i_id = (void*)strtol( id, NULL, 0 );
1254             msg_Dbg( p_httpt, "requested closing connection id=%s %p", id, i_id );
1255             for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1256             {
1257                 if( (void*)p_con == i_id )
1258                 {
1259                     /* XXX don't free p_con as it could be the one that it is sending ... */
1260                     p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1261                     return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "connection closed" ) );
1262                 }
1263             }
1264             return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, "invalid id" ) );
1265         }
1266         else if( !strcmp( action, "ban_ip" ) )
1267         {
1268             char id[128];
1269             void *i_id;
1270             
1271             _httpd_uri_extract_value( p_request, "id", id, 512 );
1272             i_id = (void*)strtol( id, NULL, 0 );
1273             
1274             msg_Dbg( p_httpt, "requested banning ip id=%s %p", id, i_id );
1275             
1276             for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1277             {
1278                 if( (void*)p_con == i_id )
1279                 {
1280                     if( httpd_BanIP( p_httpt,inet_ntoa( p_con->sock.sin_addr ) ) == 0)
1281                         return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "IP banned" ) );
1282                     else
1283                         break;
1284                 }
1285             }
1286             
1287             return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1288         }
1289         else if( !strcmp( action, "unban_ip" ) )
1290         {
1291             char id[128];
1292             
1293             _httpd_uri_extract_value( p_request, "id", id, 512 );
1294             msg_Dbg( p_httpt, "requested unbanning ip %s", id);
1295             
1296             if( httpd_UnbanIP( p_httpt, httpd_GetbannedIP ( p_httpt, id ) ) == 0)
1297                 return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "IP Unbanned" ) );
1298             else
1299                 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1300         }
1301         else if( !strcmp( action, "close_connection_and_ban_ip" ) )           
1302         {
1303             char id[128];
1304             void *i_id;
1305             
1306             _httpd_uri_extract_value( p_request, "id", id, 512 );
1307             i_id = (void*)strtol( id, NULL, 0 );
1308             msg_Dbg( p_httpt, "requested closing connection and banning ip id=%s %p", id, i_id );
1309             for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1310             {
1311                 if( (void*)p_con == i_id )
1312                 {
1313                     /* XXX don't free p_con as it could be the one that it is sending ... */
1314                     p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1315                     
1316                     if( httpd_BanIP( p_httpt,inet_ntoa( p_con->sock.sin_addr ) ) == 0)
1317                         return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "Connection closed and IP banned" ) );
1318                     else
1319                         break;
1320                 }
1321
1322             }
1323             return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, "invalid id" ) );
1324             
1325             
1326             return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1327         }       
1328         else
1329         {
1330             return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1331         }
1332     }
1333     else
1334     {
1335         return( _httpd_page_admin_get_status( p_args, pp_data, pi_data ) );
1336     }
1337
1338     return VLC_SUCCESS;
1339 }
1340
1341 static int httpd_BanIP( httpd_sys_t *p_httpt, char * psz_new_banned_ip)
1342 {
1343     httpd_banned_ip_t *p_new_banned_ip ;
1344     
1345     p_new_banned_ip = malloc( sizeof( httpd_banned_ip_t ) );    
1346     if( !p_new_banned_ip )
1347     {
1348         return -1;
1349     }
1350     p_new_banned_ip->p_next=NULL;
1351     p_new_banned_ip->psz_ip = malloc( strlen( psz_new_banned_ip ) + 1 );
1352     if( !p_new_banned_ip->psz_ip )
1353     {
1354         return -2;
1355     }
1356     
1357     strcpy( p_new_banned_ip->psz_ip, psz_new_banned_ip );
1358     
1359     msg_Dbg( p_httpt, "Banning IP %s", psz_new_banned_ip );
1360     
1361     if( p_httpt->p_first_banned_ip )
1362     {
1363         httpd_banned_ip_t *p_last;
1364         
1365         p_last = p_httpt->p_first_banned_ip;
1366         while( p_last->p_next )
1367         {
1368             p_last = p_last->p_next;
1369         }
1370         
1371         p_last->p_next = p_new_banned_ip;
1372         p_new_banned_ip->p_prev = p_last;
1373     }
1374     else
1375     {
1376         p_new_banned_ip->p_prev = NULL;
1377         
1378         p_httpt->p_first_banned_ip = p_new_banned_ip;
1379     }
1380     
1381     p_httpt->i_banned_ip_count++;
1382     return 0;
1383 }
1384
1385 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *p_httpt, char *psz_ip )
1386 {
1387     httpd_banned_ip_t *p_ip;
1388     
1389     p_ip = p_httpt->p_first_banned_ip;
1390     
1391     while( p_ip)
1392     {
1393         if( strcmp( psz_ip, p_ip->psz_ip ) == 0 )
1394         {
1395             return p_ip;
1396         }
1397         p_ip = p_ip->p_next;
1398     }
1399     
1400     return NULL;
1401 }
1402
1403 static int httpd_UnbanIP( httpd_sys_t *p_httpt, httpd_banned_ip_t *p_banned_ip )
1404 {
1405     if(!p_banned_ip)
1406     {
1407         return -1;
1408     }
1409     
1410     msg_Dbg( p_httpt, "Unbanning IP %s",p_banned_ip->psz_ip);
1411     
1412     /* first cut out from list */
1413     if( p_banned_ip->p_prev )
1414     {
1415         p_banned_ip->p_prev->p_next = p_banned_ip->p_next;
1416     }
1417     else
1418     {
1419         p_httpt->p_first_banned_ip = p_banned_ip->p_next;
1420     }
1421     
1422     if( p_banned_ip->p_next )
1423     {
1424         p_banned_ip->p_next->p_prev = p_banned_ip->p_prev;
1425     }
1426     
1427     FREE( p_banned_ip->psz_ip );
1428     FREE( p_banned_ip );
1429     
1430     p_httpt->i_banned_ip_count--;
1431     
1432     return 0;
1433     
1434 }
1435
1436 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
1437 {
1438     httpd_connection_t *p_con;
1439
1440     msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
1441     
1442     /* verify if it's a banned ip */
1443     if(httpd_GetbannedIP( p_httpt,inet_ntoa( p_sock->sin_addr ) ) )
1444     {
1445         msg_Dbg( p_httpt, "Ip %s banned : closing connection", inet_ntoa( p_sock->sin_addr ) );
1446         close(fd);
1447         return;
1448     }
1449         
1450     /* create a new connection and link it */
1451     p_con = malloc( sizeof( httpd_connection_t ) );
1452     p_con->i_state  = HTTPD_CONNECTION_RECEIVING_REQUEST;
1453     p_con->fd       = fd;
1454     p_con->i_last_activity_date = mdate();
1455
1456     p_con->sock     = *p_sock;
1457     p_con->psz_file = NULL;
1458     p_con->i_http_error = 0;
1459     p_con->psz_user = NULL;
1460     p_con->psz_password = NULL;
1461     p_con->p_file   = NULL;
1462
1463     p_con->i_request_size = 0;
1464     p_con->p_request = NULL;
1465
1466     p_con->i_buffer = 0;
1467     p_con->i_buffer_size = 8096;
1468     p_con->p_buffer = malloc( p_con->i_buffer_size );
1469
1470     p_con->i_stream_pos = 0; // updated by httpd_thread */
1471     p_con->p_next = NULL;
1472
1473     if( p_httpt->p_first_connection )
1474     {
1475         httpd_connection_t *p_last;
1476
1477         p_last = p_httpt->p_first_connection;
1478         while( p_last->p_next )
1479         {
1480             p_last = p_last->p_next;
1481         }
1482
1483         p_last->p_next = p_con;
1484         p_con->p_prev = p_last;
1485     }
1486     else
1487     {
1488         p_con->p_prev = NULL;
1489
1490         p_httpt->p_first_connection = p_con;
1491     }
1492
1493     p_httpt->i_connection_count++;
1494 }
1495
1496 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1497 {
1498     msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1499
1500     p_httpt->i_connection_count--;
1501     /* first cut out from list */
1502     if( p_con->p_prev )
1503     {
1504         p_con->p_prev->p_next = p_con->p_next;
1505     }
1506     else
1507     {
1508         p_httpt->p_first_connection = p_con->p_next;
1509     }
1510
1511     if( p_con->p_next )
1512     {
1513         p_con->p_next->p_prev = p_con->p_prev;
1514     }
1515
1516     if( p_con->p_file ) p_con->p_file->i_ref--;
1517     FREE( p_con->psz_file );
1518
1519     FREE( p_con->p_buffer );
1520     SOCKET_CLOSE( p_con->fd );
1521
1522     FREE( p_con->psz_user );
1523     FREE( p_con->psz_password );
1524
1525     FREE( p_con->p_request );
1526     free( p_con );
1527 }
1528
1529 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1530 {
1531     char *p = *pp_buffer;
1532     int i;
1533
1534     while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1535     {
1536         p++;
1537     }
1538
1539     i = 0;
1540     for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1541     {
1542         word[i] = *p;
1543     }
1544
1545     word[__MIN( i, i_word_max -1 )] = '\0';
1546
1547     *pp_buffer = p;
1548
1549 }
1550 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1551 {
1552     char *p;
1553
1554     for( p = *pp_buffer; p < p_end; p++ )
1555     {
1556         if( p + 1 < p_end && *p == '\n' )
1557         {
1558             *pp_buffer = p + 1;
1559             return VLC_SUCCESS;
1560         }
1561         if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1562         {
1563             *pp_buffer = p + 2;
1564             return VLC_SUCCESS;
1565         }
1566     }
1567     *pp_buffer = p_end;
1568     return VLC_EGENERIC;
1569 }
1570
1571 //char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1572 static void b64_decode( char *dest, char *src )
1573 {
1574     int  i_level;
1575     int  last = 0;
1576     int  b64[256] = {
1577         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
1578         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
1579         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
1580         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
1581         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
1582         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
1583         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
1584         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
1585         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
1586         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
1587         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
1588         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
1589         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
1590         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
1591         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
1592         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
1593         };
1594
1595     for( i_level = 0; *src != '\0'; src++ )
1596     {
1597         int  c;
1598
1599         c = b64[(unsigned int)*src];
1600         if( c == -1 )
1601         {
1602             src++;
1603             continue;
1604         }
1605
1606         switch( i_level )
1607         {
1608             case 0:
1609                 i_level++;
1610                 break;
1611             case 1:
1612                 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1613                 i_level++;
1614                 break;
1615             case 2:
1616                 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1617                 i_level++;
1618                 break;
1619             case 3:
1620                 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1621                 i_level = 0;
1622         }
1623         last = c;
1624     }
1625
1626     *dest = '\0';
1627 }
1628
1629 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1630 {
1631     char *psz_status;
1632     char *p, *p_end;
1633
1634     int  i;
1635     char command[32];
1636     char url[1024];
1637     char version[32];
1638     char user[512] = "";
1639     char password[512] = "";
1640
1641     //msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1642
1643
1644     p = p_con->p_buffer;
1645     p_end = p + strlen( p ) + 1;
1646
1647     httpd_RequestGetWord( command, 32, &p, p_end );
1648     httpd_RequestGetWord( url, 1024, &p, p_end );
1649     httpd_RequestGetWord( version, 32, &p, p_end );
1650     //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1651
1652     p_con->p_request      = NULL;
1653     p_con->i_request_size = 0;
1654     if( !strcmp( command, "GET" ) )
1655     {
1656         p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1657     }
1658     else if( !strcmp( command, "POST" ))
1659     {
1660         p_con->i_method = HTTPD_CONNECTION_METHOD_POST;
1661     }
1662     else
1663     {
1664         /* unimplemented */
1665         p_con->psz_file = strdup( "/501.html" );
1666         p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1667         p_con->i_http_error = 501;
1668         goto create_header;
1669     }
1670
1671     if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1672     {
1673         p_con->psz_file = strdup( "/505.html" );
1674         p_con->i_http_error = 505;
1675
1676         goto create_header;
1677     }
1678
1679     /* parse headers */
1680     for( ;; )
1681     {
1682         char header[1024];
1683
1684         if( httpd_RequestNextLine( &p, p_end ) )
1685         {
1686             //msg_Dbg( p_httpt, "failled new line" );
1687             break;;
1688         }
1689         //msg_Dbg( p_httpt, "new line=%s", p );
1690
1691         httpd_RequestGetWord( header, 1024, &p, p_end );
1692         if( !strcmp( header, "\r\n" ) || !strcmp( header, "\n" ) )
1693         {
1694             break;
1695         }
1696
1697         if( !strcmp( header, "Authorization:" ) )
1698         {
1699             char method[32];
1700
1701             httpd_RequestGetWord( method, 32, &p, p_end );
1702             if( !strcasecmp( method, "BASIC" ) )
1703             {
1704                 char basic[1024];
1705                 char decoded[1024];
1706
1707                 httpd_RequestGetWord( basic, 1024, &p, p_end );
1708                 //msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1709                 b64_decode( decoded, basic );
1710
1711                 //msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1712                 if( strchr( decoded, ':' ) )
1713                 {
1714                     char *p = strchr( decoded, ':' );
1715
1716                     p[0] = '\0'; p++;
1717                     strcpy( user, decoded );
1718                     strcpy( password, p );
1719                 }
1720             }
1721         }
1722     }
1723
1724     if( strchr( url, '?' ) )
1725     {
1726         char *p_request = strchr( url, '?' );
1727         *p_request++ = '\0';
1728         p_con->psz_file = strdup( url );
1729         p_con->p_request = strdup( p_request );
1730         p_con->i_request_size = strlen( p_con->p_request );
1731     }
1732     else
1733     {
1734         p_con->psz_file = strdup( url );
1735     }
1736
1737     /* fix p_request */
1738     if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1739     {
1740         char *p_request;
1741         if( strstr( p_con->p_buffer, "\r\n\r\n" ) )
1742         {
1743             p_request = strstr( p_con->p_buffer, "\r\n\r\n" ) + 4;
1744         }
1745         else if( strstr( p_con->p_buffer, "\n\n" ) )
1746         {
1747             p_request = strstr( p_con->p_buffer, "\n\n" ) + 2;
1748         }
1749         else
1750         {
1751             p_request = NULL;
1752         }
1753         if( p_request && p_request < p_end )
1754         {
1755             p_con->i_request_size = p_end - p_request;
1756             p_con->p_request = malloc( p_con->i_request_size + 1);
1757
1758             memcpy( p_con->p_request,
1759                     p_request,
1760                     p_con->i_request_size );
1761
1762             p_con->p_request[p_con->i_request_size] = '\0';
1763         }
1764     }
1765     p_con->i_http_error = 200;
1766
1767 create_header:
1768     //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1769     FREE( p_con->p_buffer );
1770     p_con->i_buffer = 0;
1771     p_con->i_buffer_size = 0;
1772
1773     //vlc_mutex_lock( &p_httpt->file_lock );
1774 search_file:
1775     /* search file */
1776     p_con->p_file = NULL;
1777     for( i = 0; i < p_httpt->i_file_count; i++ )
1778     {
1779         if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1780         {
1781             if( p_httpt->file[i]->b_stream ||
1782                 ( p_con->i_method == HTTPD_CONNECTION_METHOD_GET  && p_httpt->file[i]->pf_get ) ||
1783                 ( p_con->i_method == HTTPD_CONNECTION_METHOD_POST && p_httpt->file[i]->pf_post ) )
1784             {
1785                 p_con->p_file = p_httpt->file[i];
1786                 break;
1787             }
1788         }
1789     }
1790
1791     if( !p_con->p_file )
1792     {
1793         p_con->psz_file = strdup( "/404.html" );
1794         p_con->i_http_error = 404;
1795
1796         /* XXX be sure that "/404.html" exist else ... */
1797         goto search_file;
1798     }
1799
1800     if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1801     {
1802         if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) )
1803         {
1804             p_con->psz_file = strdup( "/401.html" );
1805             strcpy( user, p_con->p_file->psz_user );
1806             p_con->i_http_error = 401;
1807
1808             /* XXX do not put password on 404 else ... */
1809             goto search_file;
1810         }
1811     }
1812
1813     p_con->p_file->i_ref++;
1814 //    vlc_mutex_unlock( &p_httpt->file_lock );
1815
1816     switch( p_con->i_http_error )
1817     {
1818         case 200:
1819             psz_status = "OK";
1820             break;
1821
1822         case 401:
1823             psz_status = "Authorization Required";
1824             break;
1825         default:
1826             psz_status = "Unknown";
1827             break;
1828     }
1829
1830     p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1831
1832     /* we send stream header with this one */
1833     if( p_con->i_http_error == 200 && p_con->p_file->b_stream )
1834     {
1835         p_con->i_buffer_size = 4096 + p_con->p_file->i_header_size;
1836     }
1837
1838     p_con->i_buffer_size = 4096;
1839     p_con->i_buffer = 0;  
1840     p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1841
1842     p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1843     p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1844     if( p_con->i_http_error == 401 )
1845     {
1846         p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1847     }
1848     p += sprintf( p, "Cache-Control: no-cache\r\n" );
1849     p += sprintf( p, "\r\n" );
1850
1851     p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1;
1852
1853     if( p_con->i_http_error == 200 && p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 )
1854     {
1855         /* add stream header */
1856         memcpy( &p_con->p_buffer[p_con->i_buffer_size],
1857                 p_con->p_file->p_header,
1858                 p_con->p_file->i_header_size );
1859         p_con->i_buffer_size += p_con->p_file->i_header_size;
1860     }
1861
1862     //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1863 }
1864 #define HTTPD_STREAM_PACKET 10000
1865 static void httpd_Thread( httpd_sys_t *p_httpt )
1866 {
1867     httpd_file_t    *p_page_admin;
1868     httpd_file_t    *p_page_401;
1869     httpd_file_t    *p_page_404;
1870
1871     httpd_connection_t *p_con;
1872
1873     msg_Info( p_httpt, "httpd started" );
1874
1875     p_page_401 = _RegisterFile( p_httpt,
1876                                 "/401.html", "text/html",
1877                                 NULL, NULL,
1878                                 httpd_page_401_get,
1879                                 NULL,
1880                                 (httpd_file_callback_args_t*)NULL );
1881     p_page_404 = _RegisterFile( p_httpt,
1882                                 "/404.html", "text/html",
1883                                 NULL, NULL,
1884                                 httpd_page_404_get,
1885                                 NULL,
1886                                 (httpd_file_callback_args_t*)NULL );
1887     p_page_admin = _RegisterFile( p_httpt,
1888                                   "/admin.html", "text/html",
1889                                   "admin", "salut",
1890                                   httpd_page_admin_get,
1891                                   NULL,
1892                                   (httpd_file_callback_args_t*)p_httpt );
1893
1894     while( !p_httpt->b_die )
1895     {
1896         struct timeval  timeout;
1897         fd_set          fds_read;
1898         fd_set          fds_write;
1899         int             i_handle_max = 0;
1900         int             i_ret;
1901         int i;
1902         if( p_httpt->i_host_count <= 0 )
1903         {
1904             msleep( 100 * 1000 );
1905             continue;
1906         }
1907
1908         /* we will create a socket set with host and connection */
1909         FD_ZERO( &fds_read );
1910         FD_ZERO( &fds_write );
1911
1912         vlc_mutex_lock( &p_httpt->host_lock );
1913         vlc_mutex_lock( &p_httpt->connection_lock );
1914         for( i = 0; i < p_httpt->i_host_count; i++ )
1915         {
1916             FD_SET( p_httpt->host[i]->fd, &fds_read );
1917             i_handle_max = __MAX( i_handle_max, p_httpt->host[i]->fd );
1918         }
1919         for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1920         {
1921             /* no more than 10s of inactivity */
1922             if( p_con->i_last_activity_date + (mtime_t)HTTPD_CONNECTION_MAX_UNUSED < mdate() ||
1923                 p_con->i_state == HTTPD_CONNECTION_TO_BE_CLOSED)
1924             {
1925                 httpd_connection_t *p_next = p_con->p_next;
1926
1927                 msg_Dbg( p_httpt,  "close unused connection" );
1928                 httpd_ConnnectionClose( p_httpt, p_con );
1929                 p_con = p_next;
1930                 continue;
1931             }
1932
1933             if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM && p_con->i_stream_pos + HTTPD_STREAM_PACKET >= p_con->p_file->i_buffer_pos )
1934             {
1935                 p_con = p_con->p_next;
1936                 continue;
1937             }
1938
1939             if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1940             {
1941                 FD_SET( p_con->fd, &fds_read );
1942             }
1943             else
1944             {
1945                 FD_SET( p_con->fd, &fds_write );
1946             }
1947             i_handle_max = __MAX( i_handle_max, p_con->fd );
1948
1949             p_con = p_con->p_next;
1950         }
1951         vlc_mutex_unlock( &p_httpt->host_lock );
1952         vlc_mutex_unlock( &p_httpt->connection_lock );
1953
1954         /* we will wait 0.5s */
1955         timeout.tv_sec = 0;
1956         timeout.tv_usec = 500*1000;
1957
1958         i_ret = select( i_handle_max + 1, 
1959                         &fds_read,
1960                         &fds_write,
1961                         NULL,
1962                         &timeout );
1963         if( i_ret == -1 && errno != EINTR )
1964         {
1965             msg_Warn( p_httpt, "cannot select sockets" );
1966             msleep( 1000 );
1967             continue;
1968         }
1969         /*FIXME : < 0 is necessary for medialive plugin (it was <=0 before), but why ?*/       
1970         if( i_ret < 0 )
1971         {
1972 //            msg_Dbg( p_httpt, "waiting..." );
1973             continue;
1974         }
1975
1976         vlc_mutex_lock( &p_httpt->host_lock );
1977         /* accept/refuse new connection */
1978         for( i = 0; i < p_httpt->i_host_count; i++ )
1979         {
1980             int     i_sock_size = sizeof( struct sockaddr_in );
1981             struct  sockaddr_in sock;
1982             int     fd;
1983
1984             fd = accept( p_httpt->host[i]->fd, (struct sockaddr *)&sock,
1985                          &i_sock_size );
1986             if( fd > 0 )
1987             {
1988 #if defined( WIN32 ) || defined( UNDER_CE )
1989                 {
1990                     unsigned long i_dummy = 1;
1991                     ioctlsocket( fd, FIONBIO, &i_dummy );
1992                 }
1993 #else
1994                 fcntl( fd, F_SETFL, O_NONBLOCK );
1995 #endif
1996
1997                 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
1998                 {
1999                     msg_Warn( p_httpt, "max connection reached" );
2000                     SOCKET_CLOSE( fd );
2001                     continue;
2002                 }
2003                 /* create a new connection and link it */
2004                 httpd_ConnnectionNew( p_httpt, fd, &sock );
2005
2006             }
2007         }
2008         vlc_mutex_unlock( &p_httpt->host_lock );
2009
2010         vlc_mutex_lock( &p_httpt->file_lock );
2011         /* now do work for all connections */
2012         for( p_con = p_httpt->p_first_connection; p_con != NULL; )
2013         {
2014             if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
2015             {
2016                 int i_len;
2017                 /* read data */
2018                 i_len = recv( p_con->fd,
2019                               p_con->p_buffer + p_con->i_buffer,
2020                               p_con->i_buffer_size - p_con->i_buffer, 0 );
2021
2022
2023 #if defined( WIN32 ) || defined( UNDER_CE )
2024                 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) ||
2025 #else
2026                 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) ||
2027 #endif
2028                     ( i_len == 0 ) )
2029                 {
2030                     httpd_connection_t *p_next = p_con->p_next;
2031
2032                     httpd_ConnnectionClose( p_httpt, p_con );
2033                     p_con = p_next;
2034                 }
2035                 else if( i_len > 0 )
2036                 {
2037                     uint8_t *ptr;
2038                     p_con->i_last_activity_date = mdate();
2039                     p_con->i_buffer += i_len;
2040
2041                     ptr = p_con->p_buffer + p_con->i_buffer;
2042
2043                     if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
2044                         ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
2045                         p_con->i_buffer >= p_con->i_buffer_size )
2046                     {
2047                         p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
2048                         httpd_ConnectionParseRequest( p_httpt, p_con );
2049                     }
2050
2051                     p_con = p_con->p_next;
2052                 }
2053                 else
2054                 {
2055                     p_con = p_con->p_next;
2056                 }
2057                 continue;   /* just for clarity */
2058             }
2059             else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
2060             {
2061                 int i_len;
2062
2063                 /* write data */
2064                 if( p_con->i_buffer_size - p_con->i_buffer > 0 )
2065                 {
2066                     i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
2067                 }
2068                 else
2069                 {
2070                     i_len = 0;
2071                 }
2072 //                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 );
2073
2074 #if defined( WIN32 ) || defined( UNDER_CE )
2075                 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) ||
2076 #else
2077                 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) ||
2078 #endif
2079                     ( i_len == 0 ) )
2080                 {
2081                     httpd_connection_t *p_next = p_con->p_next;
2082
2083                     httpd_ConnnectionClose( p_httpt, p_con );
2084                     p_con = p_next;
2085                 }
2086                 else if( i_len > 0 )
2087                 {
2088                     p_con->i_last_activity_date = mdate();
2089                     p_con->i_buffer += i_len;
2090
2091                     if( p_con->i_buffer >= p_con->i_buffer_size )
2092                     {
2093                         if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
2094                         {
2095                             p_con->i_buffer_size = 0;
2096                             p_con->i_buffer = 0;
2097                             FREE( p_con->p_buffer );
2098
2099                             if( !p_con->p_file->b_stream )
2100                             {
2101                                 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
2102                                 if( p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
2103                                 {
2104                                     p_con->p_file->pf_get( p_con->p_file->p_sys,
2105                                                            p_con->p_request, p_con->i_request_size,
2106                                                            &p_con->p_buffer, &p_con->i_buffer_size );
2107                                 }
2108                                 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
2109                                 {
2110                                     p_con->p_file->pf_post( p_con->p_file->p_sys,
2111                                                             p_con->p_request, p_con->i_request_size,
2112                                                             &p_con->p_buffer, &p_con->i_buffer_size );
2113                                 }
2114                                 else
2115                                 {
2116                                     p_con->p_buffer = NULL;
2117                                     p_con->i_buffer_size = 0;
2118                                 }
2119                             }
2120                             else
2121                             {
2122                                 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
2123                                 p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos;
2124                             }
2125                             p_con = p_con->p_next;
2126                         }
2127                         else
2128                         {
2129                             httpd_connection_t *p_next = p_con->p_next;
2130
2131                             httpd_ConnnectionClose( p_httpt, p_con );
2132                             p_con = p_next;
2133                         }
2134                     }
2135                     else
2136                     {
2137                         p_con = p_con->p_next;
2138                     }
2139                 }
2140                 else
2141                 {
2142                     p_con = p_con->p_next;
2143                 }
2144                 continue;   /* just for clarity */
2145             }
2146             else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
2147             {
2148                 httpd_stream_t *p_stream = p_con->p_file;
2149                 int i_send;
2150                 int i_write;
2151
2152                 if( p_con->i_stream_pos < p_stream->i_buffer_pos )
2153                 {
2154                     int i_pos;
2155                     /* check if this p_con aren't to late */
2156                     if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos )
2157                     {
2158                         fprintf(stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n", p_con->i_stream_pos, p_stream->i_buffer_pos  );
2159                         p_con->i_stream_pos = p_stream->i_buffer_last_pos;
2160                     }
2161
2162                     i_pos = p_con->i_stream_pos % p_stream->i_buffer_size;
2163                     /* size until end of buffer */
2164                     i_write = p_stream->i_buffer_size - i_pos;
2165                     /* is it more than valid data */
2166                     if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos )
2167                     {
2168                         i_write = p_stream->i_buffer_pos - p_con->i_stream_pos;
2169                     }
2170                     /* limit to HTTPD_STREAM_PACKET */
2171                     if( i_write > HTTPD_STREAM_PACKET )
2172                     {
2173                         i_write = HTTPD_STREAM_PACKET;
2174                     }
2175                     i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 );
2176
2177 #if defined( WIN32 ) || defined( UNDER_CE )
2178                     if( ( i_send < 0 && WSAGetLastError() != WSAEWOULDBLOCK )||
2179 #else
2180                     if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )||
2181 #endif
2182                         ( i_send == 0 ) )
2183                     {
2184                         httpd_connection_t *p_next = p_con->p_next;
2185
2186                         httpd_ConnnectionClose( p_httpt, p_con );
2187                         p_con = p_next;
2188                         continue;
2189                     }
2190                     else if( i_send > 0 )
2191                     {
2192                         msg_Dbg( p_httpt, "Sending %d bytes",i_send );
2193                         p_con->i_last_activity_date = mdate();
2194                         p_con->i_stream_pos += i_send;
2195                     }
2196                 }
2197                 p_con = p_con->p_next;
2198                 continue;   /* just for clarity */
2199             }
2200             else if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
2201             {
2202                 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
2203                 p_con = p_con->p_next;
2204             }
2205         }   /* for over connection */
2206
2207         vlc_mutex_unlock( &p_httpt->file_lock );
2208     }
2209     msg_Info( p_httpt, "httpd stopped" );
2210
2211     _UnregisterFile( p_httpt, p_page_401 );
2212     _UnregisterFile( p_httpt, p_page_404 );
2213     _UnregisterFile( p_httpt, p_page_admin );
2214 }