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