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