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