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