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