1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: httpd.c,v 1.31 2004/02/05 19:51:46 fenrir Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
28 * - make that two distinct host:port use different daemon
29 *****************************************************************************/
40 #ifdef HAVE_SYS_TIME_H
41 # include <sys/time.h>
48 #if defined( UNDER_CE )
50 #elif defined( WIN32 )
51 # include <winsock2.h>
52 # include <ws2tcpip.h>
54 # define IN_MULTICAST(a) IN_CLASSD(a)
57 # include <netdb.h> /* hostent ... */
58 # include <sys/socket.h>
59 # include <netinet/in.h>
60 # ifdef HAVE_ARPA_INET_H
61 # include <arpa/inet.h> /* inet_ntoa(), inet_aton() */
68 # define INADDR_ANY 0x00000000
71 # define INADDR_NONE 0xFFFFFFFF
74 #define LISTEN_BACKLOG 100
75 #define HTTPD_MAX_CONNECTION 512
76 #define HTTPD_CONNECTION_MAX_UNUSED 10000000
79 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
81 #if defined( WIN32 ) || defined( UNDER_CE )
82 #define SOCKET_CLOSE(a) closesocket(a)
84 #define SOCKET_CLOSE(a) close(a)
87 /*****************************************************************************
89 *****************************************************************************/
90 static int Open ( vlc_object_t * );
91 static void Close ( vlc_object_t * );
93 /*****************************************************************************
95 *****************************************************************************/
97 set_description( _("HTTP 1.0 daemon") );
98 set_capability( "httpd", 42 );
99 set_callbacks( Open, Close );
100 var_Create( p_module->p_libvlc, "httpd", VLC_VAR_MUTEX );
103 /*****************************************************************************
105 *****************************************************************************/
106 static httpd_host_t *RegisterHost ( httpd_t *, char *, int );
107 static void UnregisterHost ( httpd_t *, httpd_host_t * );
109 static httpd_file_t *RegisterFile ( httpd_t *,
110 char *psz_file, char *psz_mime,
111 char *psz_user, char *psz_password,
112 httpd_file_callback pf_get,
113 httpd_file_callback pf_post,
114 httpd_file_callback_args_t *p_args );
115 static void UnregisterFile ( httpd_t *, httpd_file_t * );
118 #define httpd_stream_t httpd_file_t
120 static httpd_stream_t *RegisterStream ( httpd_t *,
121 char *psz_file, char *psz_mime,
122 char *psz_user, char *psz_password );
123 static int SendStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
124 static int HeaderStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
125 static void UnregisterStream( httpd_t *, httpd_stream_t* );
126 static int Control ( httpd_t *, int , void*, void* );
127 /*****************************************************************************
128 * Internal definitions
129 *****************************************************************************/
137 struct sockaddr_in sock;
142 enum httpd_authenticate_e
144 HTTPD_AUTHENTICATE_NONE = 0,
145 HTTPD_AUTHENTICATE_BASIC = 1
149 typedef httpd_file_t httpd_stream_t;
160 int i_authenticate_method;
161 char *psz_user; /* NULL if no auth */
162 char *psz_password; /* NULL if no auth */
164 vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback
165 true: it's up to the program to give data to httpd */
166 void *p_sys; /* provided for user */
167 httpd_file_callback pf_get; /* it should allocate and fill *pp_data and *pi_data */
168 httpd_file_callback pf_post; /* it should allocate and fill *pp_data and *pi_data */
172 /* circular buffer for stream only */
173 int i_buffer_size; /* buffer size, can't be reallocated smaller */
174 uint8_t *p_buffer; /* buffer */
175 int64_t i_buffer_pos; /* absolute position from begining */
176 int64_t i_buffer_last_pos; /* a new connection will start with that */
178 /* data to be send at connection time (if any) */
184 enum httpd_connection_state_e
186 HTTPD_CONNECTION_RECEIVING_REQUEST = 1,
187 HTTPD_CONNECTION_SENDING_HEADER = 2,
188 HTTPD_CONNECTION_SENDING_FILE = 3,
189 HTTPD_CONNECTION_SENDING_STREAM = 4,
190 HTTPD_CONNECTION_TO_BE_CLOSED = 5
193 enum httpd_connection_method_e
195 HTTPD_CONNECTION_METHOD_GET = 1,
196 HTTPD_CONNECTION_METHOD_POST = 2,
197 HTTPD_CONNECTION_METHOD_HEAD = 3,
198 /* cludgy, only used when parsing connection request */
199 HTTPD_CONNECTION_METHOD_ASFHEAD = 4
202 typedef struct httpd_connection_s
204 struct httpd_connection_s *p_next;
205 struct httpd_connection_s *p_prev;
207 struct sockaddr_in sock;
209 mtime_t i_last_activity_date;
212 int i_method; /* get/post */
214 char *psz_file; /* file to be send */
215 int i_http_error; /* error to be send with the file */
216 char *psz_user; /* if Authorization in the request header */
219 uint8_t *p_request; /* whith get: ?<*>, with post: main data */
222 httpd_file_t *p_file;
224 /* used while sending header and file */
227 int i_buffer; /* private */
229 /* used for stream */
230 int64_t i_stream_pos; /* absolute pos in stream */
231 } httpd_connection_t;
233 /* Linked List of banned IP */
234 typedef struct httpd_banned_ip_s
236 struct httpd_banned_ip_s *p_next;
237 struct httpd_banned_ip_s *p_prev;
249 vlc_mutex_t host_lock;
250 volatile int i_host_count;
253 vlc_mutex_t file_lock;
257 vlc_mutex_t connection_lock;
258 int i_connection_count;
259 httpd_connection_t *p_first_connection;
261 vlc_mutex_t ban_lock;
262 int i_banned_ip_count;
263 httpd_banned_ip_t *p_first_banned_ip;
266 static void httpd_Thread( httpd_sys_t *p_httpt );
267 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
268 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
269 static int httpd_UnbanIP( httpd_sys_t *, httpd_banned_ip_t *);
271 static int httpd_BanIP( httpd_sys_t *, char *);
273 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *, char * );
275 /*****************************************************************************
277 *****************************************************************************/
279 static int Open( vlc_object_t *p_this )
281 httpd_t *p_httpd = (httpd_t*)p_this;
282 httpd_sys_t *p_httpt;
284 /* Launch httpt thread */
285 if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
287 msg_Err( p_this, "out of memory" );
288 return( VLC_EGENERIC );
294 /* init httpt_t structure */
295 if( vlc_mutex_init( p_httpd, &p_httpt->host_lock ) )
297 msg_Err( p_httpd, "Error in mutex creation");
298 return( VLC_EGENERIC );
300 p_httpt->i_host_count = 0;
301 p_httpt->host = NULL;
303 if( vlc_mutex_init( p_httpd, &p_httpt->file_lock ) )
305 msg_Err( p_httpd, "Error in mutex creation");
306 return( VLC_EGENERIC );
308 p_httpt->i_file_count = 0;
309 p_httpt->file = NULL;
311 if( vlc_mutex_init( p_httpd, &p_httpt->connection_lock ) )
313 msg_Err( p_httpd, "Error in mutex creation");
314 return( VLC_EGENERIC );
316 p_httpt->i_connection_count = 0;
317 p_httpt->p_first_connection = NULL;
319 if( vlc_mutex_init( p_httpd, &p_httpt->ban_lock ) )
321 msg_Err( p_httpd, "Error in mutex creation");
322 return( VLC_EGENERIC );
325 p_httpt->i_banned_ip_count = 0;
326 p_httpt->p_first_banned_ip = NULL;
328 /* start the thread */
329 if( vlc_thread_create( p_httpt, "httpd thread",
330 httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
332 msg_Err( p_this, "cannot spawn http thread" );
334 vlc_mutex_destroy( &p_httpt->host_lock );
335 vlc_mutex_destroy( &p_httpt->file_lock );
336 vlc_mutex_destroy( &p_httpt->connection_lock );
337 vlc_mutex_destroy( &p_httpt->ban_lock );
339 vlc_object_destroy( p_httpt );
340 return( VLC_EGENERIC );
343 msg_Info( p_httpd, "http thread launched" );
345 p_httpd->p_sys = p_httpt;
346 p_httpd->pf_register_host = RegisterHost;
347 p_httpd->pf_unregister_host = UnregisterHost;
348 p_httpd->pf_register_file = RegisterFile;
349 p_httpd->pf_unregister_file = UnregisterFile;
350 p_httpd->pf_register_stream = RegisterStream;
351 p_httpd->pf_header_stream = HeaderStream;
352 p_httpd->pf_send_stream = SendStream;
353 p_httpd->pf_unregister_stream=UnregisterStream;
354 p_httpd->pf_control = Control;
356 return( VLC_SUCCESS );
359 /*****************************************************************************
360 * Close: close the target
361 *****************************************************************************/
362 static void Close( vlc_object_t * p_this )
364 httpd_t *p_httpd = (httpd_t*)p_this;
365 httpd_sys_t *p_httpt = p_httpd->p_sys;
367 httpd_connection_t *p_con;
368 httpd_banned_ip_t *p_banned_ip;
373 vlc_thread_join( p_httpt );
375 /* first close all host */
376 vlc_mutex_destroy( &p_httpt->host_lock );
377 if( p_httpt->i_host_count )
379 msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
381 for( i = 0; i < p_httpt->i_host_count; i++ )
383 #define p_host p_httpt->host[i]
384 FREE( p_host->psz_host_addr );
385 SOCKET_CLOSE( p_host->fd );
390 FREE( p_httpt->host );
393 vlc_mutex_destroy( &p_httpt->file_lock );
394 if( p_httpt->i_file_count )
396 msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
398 for( i = 0; i < p_httpt->i_file_count; i++ )
400 #define p_file p_httpt->file[i]
401 FREE( p_file->psz_file );
402 FREE( p_file->psz_mime );
403 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
405 FREE( p_file->psz_user );
406 FREE( p_file->psz_password );
408 FREE( p_file->p_buffer );
413 FREE( p_httpt->file );
415 /* andd close all connection */
416 vlc_mutex_destroy( &p_httpt->connection_lock );
417 if( p_httpt->i_connection_count )
419 msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
421 while( ( p_con = p_httpt->p_first_connection ) )
423 httpd_ConnnectionClose( p_httpt, p_con );
426 /* Free all banned IP */
427 vlc_mutex_destroy( &p_httpt->ban_lock );
428 while( ( p_banned_ip = p_httpt->p_first_banned_ip))
430 httpd_UnbanIP(p_httpt,p_banned_ip);
433 msg_Info( p_httpd, "httpd instance closed" );
434 vlc_object_destroy( p_httpt );
438 /****************************************************************************
439 ****************************************************************************
442 ****************************************************************************
443 ****************************************************************************/
444 static int BuildAddr( struct sockaddr_in * p_socket,
445 const char * psz_address, int i_port )
448 memset( p_socket, 0, sizeof( struct sockaddr_in ) );
449 p_socket->sin_family = AF_INET; /* family */
450 p_socket->sin_port = htons( (uint16_t)i_port );
453 p_socket->sin_addr.s_addr = INADDR_ANY;
457 struct hostent * p_hostent;
459 /* Try to convert address directly from in_addr - this will work if
460 * psz_address is dotted decimal. */
461 #ifdef HAVE_ARPA_INET_H
462 if( !inet_aton( psz_address, &p_socket->sin_addr ) )
464 p_socket->sin_addr.s_addr = inet_addr( psz_address );
465 if( p_socket->sin_addr.s_addr == INADDR_NONE )
468 /* We have a fqdn, try to find its address */
469 if ( (p_hostent = gethostbyname( psz_address )) == NULL )
474 /* Copy the first address of the host in the socket address */
475 memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
476 p_hostent->h_length );
484 * listen on a host for a httpd instance
487 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
489 httpd_host_t *p_host;
490 struct sockaddr_in sock;
494 #if !defined( WIN32 ) && !defined( UNDER_CE )
498 if( BuildAddr( &sock, psz_host_addr, i_port ) )
500 msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
504 /* is it already declared ? */
505 vlc_mutex_lock( &p_httpt->host_lock );
506 for( i = 0; i < p_httpt->i_host_count; i++ )
508 if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
509 ( p_httpt->host[i]->sock.sin_addr.s_addr == INADDR_ANY ||
510 p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr ) )
516 if( i < p_httpt->i_host_count )
518 /* yes, increment ref count and succed */
519 p_httpt->host[i]->i_ref++;
520 vlc_mutex_unlock( &p_httpt->host_lock );
521 return( p_httpt->host[i] );
524 /* need to add a new listening socket */
527 fd = socket( AF_INET, SOCK_STREAM, 0 );
530 msg_Err( p_httpt, "cannot open socket" );
535 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
536 (void *) &i_opt, sizeof( i_opt ) ) < 0 )
538 msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
541 if( bind( fd, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 )
543 msg_Err( p_httpt, "cannot bind socket" );
546 /* set to non-blocking */
547 #if defined( WIN32 ) || defined( UNDER_CE )
549 unsigned long i_dummy = 1;
550 if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
552 msg_Err( p_httpt, "cannot set socket to non-blocking mode" );
557 if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
559 msg_Err( p_httpt, "cannot F_GETFL socket" );
562 if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
564 msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
569 if( listen( fd, LISTEN_BACKLOG ) < 0 )
571 msg_Err( p_httpt, "cannot listen socket" );
577 p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) );
581 p_httpt->host = malloc( sizeof( httpd_host_t *) );
585 msg_Err( p_httpt, "Out of memory" );
588 p_host = malloc( sizeof( httpd_host_t ) );
591 msg_Err( p_httpt, "Out of memory" );
595 p_host->psz_host_addr = strdup( psz_host_addr );
596 if( !p_host->psz_host_addr )
598 msg_Err( p_httpt, "Out of memory" );
601 p_host->i_port = i_port;
605 p_httpt->host[p_httpt->i_host_count++] = p_host;
606 vlc_mutex_unlock( &p_httpt->host_lock );
611 vlc_mutex_unlock( &p_httpt->host_lock );
618 static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
620 return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
624 * remove a listening host for an httpd instance
626 static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
630 vlc_mutex_lock( &p_httpt->host_lock );
631 for( i = 0; i < p_httpt->i_host_count; i++ )
633 if( p_httpt->host[i] == p_host )
638 if( i >= p_httpt->i_host_count )
640 vlc_mutex_unlock( &p_httpt->host_lock );
641 msg_Err( p_httpt, "cannot unregister host" );
647 if( p_host->i_ref > 0 )
650 vlc_mutex_unlock( &p_httpt->host_lock );
655 FREE( p_host->psz_host_addr );
656 SOCKET_CLOSE( p_host->fd );
660 if( p_httpt->i_host_count <= 1 )
662 FREE( p_httpt->host );
663 p_httpt->i_host_count = 0;
669 i_move = p_httpt->i_host_count - i - 1;
673 memmove( &p_httpt->host[i],
675 i_move * sizeof( httpd_host_t * ) );
678 p_httpt->i_host_count--;
679 p_httpt->host = realloc( p_httpt->host,
680 p_httpt->i_host_count * sizeof( httpd_host_t * ) );
683 msg_Err( p_httpt, "Out of memory" );
688 vlc_mutex_unlock( &p_httpt->host_lock );
690 static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
692 _UnregisterHost( p_httpd->p_sys, p_host );
696 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
699 if( p_httpt->i_file_count )
701 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) );
705 p_httpt->file = malloc( sizeof( httpd_file_t *) );
711 p_httpt->file[p_httpt->i_file_count++] = p_file;
714 static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt,
715 char *psz_file, char *psz_mime,
716 char *psz_user, char *psz_password,
717 httpd_file_callback pf_get,
718 httpd_file_callback pf_post,
719 httpd_file_callback_args_t *p_args )
721 httpd_file_t *p_file;
724 vlc_mutex_lock( &p_httpt->file_lock );
725 for( i = 0; i < p_httpt->i_file_count; i++ )
727 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
732 if( i < p_httpt->i_file_count )
734 vlc_mutex_unlock( &p_httpt->file_lock );
735 msg_Err( p_httpt, "%s already registered", psz_file );
739 p_file = malloc( sizeof( httpd_file_t ) );
742 msg_Err( p_httpt, "Out of memory" );
746 p_file->psz_file = strdup( psz_file );
747 p_file->psz_mime = strdup( psz_mime );
748 if( !p_file->psz_file || !p_file->psz_mime )
750 msg_Err( p_httpt, "Out of memory" );
753 if( psz_user && *psz_user )
755 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
756 p_file->psz_user = strdup( psz_user );
757 p_file->psz_password = strdup( psz_password );
758 if( !p_file->psz_user || !p_file->psz_password )
760 msg_Err( p_httpt, "Out of memory" );
766 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
767 p_file->psz_user = NULL;
768 p_file->psz_password = NULL;
771 p_file->b_stream = VLC_FALSE;
772 p_file->p_sys = p_args;
773 p_file->pf_get = pf_get;
774 p_file->pf_post = pf_post;
776 p_file->i_buffer_size = 0;
777 p_file->i_buffer_last_pos = 0;
778 p_file->i_buffer_pos = 0;
779 p_file->p_buffer = NULL;
781 p_file->i_header_size = 0;
782 p_file->p_header = NULL;
784 __RegisterFile( p_httpt, p_file );
786 vlc_mutex_unlock( &p_httpt->file_lock );
790 static httpd_file_t *RegisterFile( httpd_t *p_httpd,
791 char *psz_file, char *psz_mime,
792 char *psz_user, char *psz_password,
793 httpd_file_callback pf_get,
794 httpd_file_callback pf_post,
795 httpd_file_callback_args_t *p_args )
797 return( _RegisterFile( p_httpd->p_sys,
798 psz_file, psz_mime, psz_user, psz_password,
799 pf_get, pf_post, p_args ) );
802 static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt,
803 char *psz_file, char *psz_mime,
804 char *psz_user, char *psz_password )
806 httpd_stream_t *p_stream;
809 vlc_mutex_lock( &p_httpt->file_lock );
810 for( i = 0; i < p_httpt->i_file_count; i++ )
812 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
817 if( i < p_httpt->i_file_count )
819 vlc_mutex_unlock( &p_httpt->file_lock );
820 msg_Err( p_httpt, "%s already registered", psz_file );
824 p_stream = malloc( sizeof( httpd_stream_t ) );
827 msg_Err( p_httpt, "Out of memory" );
831 p_stream->psz_file = strdup( psz_file );
832 p_stream->psz_mime = strdup( psz_mime );
833 if( !p_stream->psz_file || !p_stream->psz_mime )
835 msg_Err( p_httpt, "Out of memory" );
838 if( psz_user && *psz_user )
840 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
841 p_stream->psz_user = strdup( psz_user );
842 p_stream->psz_password = strdup( psz_password );
843 if( !p_stream->psz_user || !p_stream->psz_password )
845 msg_Err( p_httpt, "Out of memory" );
851 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
852 p_stream->psz_user = NULL;
853 p_stream->psz_password = NULL;
856 p_stream->b_stream = VLC_TRUE;
857 p_stream->p_sys = NULL;
858 p_stream->pf_get = NULL;
859 p_stream->pf_post = NULL;
861 p_stream->i_buffer_size = 5*1024*1024;
862 p_stream->i_buffer_pos = 0;
863 p_stream->i_buffer_last_pos = 0;
864 p_stream->p_buffer = malloc( p_stream->i_buffer_size );
865 if( !p_stream->p_buffer )
867 msg_Err( p_httpt, "Out of memory" );
871 p_stream->i_header_size = 0;
872 p_stream->p_header = NULL;
874 __RegisterFile( p_httpt, p_stream );
876 vlc_mutex_unlock( &p_httpt->file_lock );
880 static httpd_stream_t *RegisterStream( httpd_t *p_httpd,
881 char *psz_file, char *psz_mime,
882 char *psz_user, char *psz_password )
884 return( _RegisterStream( p_httpd->p_sys,
885 psz_file, psz_mime, psz_user, psz_password ) );
888 static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
892 vlc_mutex_lock( &p_httpt->file_lock );
893 for( i = 0; i < p_httpt->i_file_count; i++ )
895 if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
900 if( i >= p_httpt->i_file_count )
902 vlc_mutex_unlock( &p_httpt->file_lock );
903 msg_Err( p_httpt, "cannot unregister file" );
907 if( p_file->i_ref > 0 )
909 httpd_connection_t *p_con;
910 /* force closing all connection for this file */
911 msg_Err( p_httpt, "closing all client connection" );
913 vlc_mutex_lock( &p_httpt->connection_lock );
914 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
916 httpd_connection_t *p_next;
918 p_next = p_con->p_next;
919 if( p_con->p_file == p_file )
921 httpd_ConnnectionClose( p_httpt, p_con );
925 vlc_mutex_unlock( &p_httpt->connection_lock );
928 FREE( p_file->psz_file );
929 FREE( p_file->psz_mime );
930 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
932 FREE( p_file->psz_user );
933 FREE( p_file->psz_password );
935 FREE( p_file->p_buffer );
936 FREE( p_file->p_header );
941 if( p_httpt->i_file_count == 1 )
943 FREE( p_httpt->file );
944 p_httpt->i_file_count = 0;
950 i_move = p_httpt->i_file_count - i - 1;
953 memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
955 p_httpt->i_file_count--;
956 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
959 vlc_mutex_unlock( &p_httpt->file_lock );
961 static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
963 _UnregisterFile( p_httpd->p_sys, p_file );
966 static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
968 _UnregisterFile( p_httpd->p_sys, p_stream );
973 static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
978 if( i_data <= 0 || p_data == NULL )
980 return( VLC_SUCCESS );
983 fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos );
985 vlc_mutex_lock( &p_httpt->file_lock );
987 /* save this pointer (to be used by new connection) */
988 p_stream->i_buffer_last_pos = p_stream->i_buffer_pos;
990 i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size;
996 i_copy = __MIN( i_count, p_stream->i_buffer_size - i_pos );
998 /* Ok, we can't go past the end of our buffer */
999 memcpy( &p_stream->p_buffer[i_pos],
1003 i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size;
1008 p_stream->i_buffer_pos += i_data;
1009 vlc_mutex_unlock( &p_httpt->file_lock );
1011 return( VLC_SUCCESS );
1014 static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
1016 return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
1019 static int HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
1021 httpd_sys_t *p_httpt = p_httpd->p_sys;
1023 vlc_mutex_lock( &p_httpt->file_lock );
1025 FREE( p_stream->p_header );
1026 if( p_data == NULL || i_data <= 0 )
1028 p_stream->i_header_size = 0;
1032 p_stream->i_header_size = i_data;
1033 p_stream->p_header = malloc( i_data );
1034 if( !p_stream->p_header )
1036 msg_Err( p_httpt, "Out of memory" );
1037 return( VLC_ENOMEM );
1039 memcpy( p_stream->p_header,
1043 vlc_mutex_unlock( &p_httpt->file_lock );
1045 return( VLC_SUCCESS );
1048 static void httpd_info_add_ss( httpd_info_t *p_info, char *name, char *value )
1050 if( p_info->i_count == 0 )
1052 p_info->info = malloc( sizeof( httpd_val_t ) );
1057 realloc( p_info->info,
1058 sizeof( httpd_val_t ) * ( p_info->i_count + 1 ) );
1064 p_info->info[p_info->i_count].psz_name = strdup( name );
1065 if( ! p_info->info[p_info->i_count].psz_name )
1069 p_info->info[p_info->i_count].psz_value = strdup( value ? value : "(null)");
1073 static void httpd_info_add_si( httpd_info_t *p_info, char *name, int i_value )
1075 char v[40]; /* Ok, int is not so long */
1077 sprintf( v, "%d", i_value );
1078 httpd_info_add_ss( p_info, name, v );
1081 static void httpd_info_add_sp( httpd_info_t *p_info, char *name, void *value )
1085 sprintf( v, "%p", value );
1086 httpd_info_add_ss( p_info, name, v );
1090 static int Control( httpd_t *p_httpd,
1091 int i_query, void *arg1, void *arg2 )
1093 httpd_sys_t *p_httpt = p_httpd->p_sys;
1094 httpd_info_t *p_info;
1095 httpd_connection_t *p_con;
1101 case HTTPD_GET_HOSTS:
1103 p_info->i_count = 0;
1104 vlc_mutex_lock( &p_httpt->host_lock );
1105 for( i = 0; i < p_httpt->i_host_count; i++ )
1107 httpd_info_add_sp( p_info,
1108 "id", p_httpt->host[i] );
1109 httpd_info_add_ss( p_info,
1110 "host", p_httpt->host[i]->psz_host_addr );
1111 httpd_info_add_ss( p_info,
1113 inet_ntoa(p_httpt->host[i]->sock.sin_addr));
1114 httpd_info_add_si( p_info,
1115 "port", p_httpt->host[i]->i_port );
1117 vlc_mutex_unlock( &p_httpt->host_lock );
1119 case HTTPD_GET_URLS:
1121 p_info->i_count = 0;
1122 /* we can't take file_lock */
1123 for( i = 0; i < p_httpt->i_file_count; i++ )
1125 httpd_info_add_sp( p_info,
1126 "id", p_httpt->file[i] );
1127 httpd_info_add_si( p_info,
1128 "stream", p_httpt->file[i]->b_stream ? 1 : 0 );
1129 httpd_info_add_ss( p_info,
1130 "url", p_httpt->file[i]->psz_file );
1131 httpd_info_add_ss( p_info,
1132 "mime", p_httpt->file[i]->psz_mime );
1133 httpd_info_add_si( p_info,
1134 "protected", p_httpt->file[i]->psz_user ? 1 : 0 );
1135 httpd_info_add_si( p_info,
1136 "used", p_httpt->file[i]->i_ref );
1139 case HTTPD_GET_CONNECTIONS:
1141 p_info->i_count = 0;
1142 /* we can't take lock */
1143 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1145 if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
1147 httpd_info_add_sp( p_info,
1149 httpd_info_add_ss( p_info,
1150 "ip", inet_ntoa( p_con->sock.sin_addr ) );
1151 httpd_info_add_ss( p_info,
1152 "url", p_con->psz_file );
1153 httpd_info_add_si( p_info,
1154 "status", p_con->i_http_error );
1160 p_info->i_count = 0;
1161 return VLC_EGENERIC;
1163 case HTTPD_SET_CLOSE:
1164 sscanf( arg1, "%p", &id );
1165 fprintf( stderr, "Control: HTTPD_SET_CLOSE: id=%p", id );
1167 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1169 if( (void*)p_con == id )
1171 /* XXX don't free p_con as it could be the one that it is sending ... */
1172 p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1176 return VLC_EGENERIC;
1179 sscanf( arg1, "%p", &id );
1180 fprintf( stderr, "Control: %p", id );
1182 return VLC_EGENERIC;
1186 /****************************************************************************/
1187 /****************************************************************************/
1188 /****************************************************************************/
1189 /****************************************************************************/
1190 /****************************************************************************/
1192 static int httpd_page_400_get( httpd_file_callback_args_t *p_args,
1193 uint8_t *p_request, int i_request,
1194 uint8_t **pp_data, int *pi_data )
1198 p = *pp_data = malloc( 1024 );
1203 p += sprintf( p, "<html>\n" );
1204 p += sprintf( p, "<head>\n" );
1205 p += sprintf( p, "<title>Error 400</title>\n" );
1206 p += sprintf( p, "</head>\n" );
1207 p += sprintf( p, "<body>\n" );
1208 p += sprintf( p, "<h1><center> 400 Bad Request</center></h1>\n" );
1209 p += sprintf( p, "<hr />\n" );
1210 p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
1211 p += sprintf( p, "</body>\n" );
1212 p += sprintf( p, "</html>\n" );
1214 *pi_data = strlen( *pp_data ) + 1;
1219 static int httpd_page_401_get( httpd_file_callback_args_t *p_args,
1220 uint8_t *p_request, int i_request,
1221 uint8_t **pp_data, int *pi_data )
1225 p = *pp_data = malloc( 1024 );
1231 p += sprintf( p, "<html>\n" );
1232 p += sprintf( p, "<head>\n" );
1233 p += sprintf( p, "<title>Error 401</title>\n" );
1234 p += sprintf( p, "</head>\n" );
1235 p += sprintf( p, "<body>\n" );
1236 p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
1237 p += sprintf( p, "<hr />\n" );
1238 p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
1239 p += sprintf( p, "</body>\n" );
1240 p += sprintf( p, "</html>\n" );
1242 *pi_data = strlen( *pp_data ) + 1;
1246 static int httpd_page_404_get( httpd_file_callback_args_t *p_args,
1247 uint8_t *p_request, int i_request,
1248 uint8_t **pp_data, int *pi_data )
1252 p = *pp_data = malloc( 1024 );
1258 p += sprintf( p, "<html>\n" );
1259 p += sprintf( p, "<head>\n" );
1260 p += sprintf( p, "<title>Error 404</title>\n" );
1261 p += sprintf( p, "</head>\n" );
1262 p += sprintf( p, "<body>\n" );
1263 p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
1264 p += sprintf( p, "<hr />\n" );
1265 p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
1266 p += sprintf( p, "</body>\n" );
1267 p += sprintf( p, "</html>\n" );
1269 *pi_data = strlen( *pp_data ) + 1;
1275 static int httpd_BanIP( httpd_sys_t *p_httpt, char * psz_new_banned_ip)
1277 httpd_banned_ip_t *p_new_banned_ip ;
1279 p_new_banned_ip = malloc( sizeof( httpd_banned_ip_t ) );
1280 if( !p_new_banned_ip )
1284 p_new_banned_ip->p_next=NULL;
1285 p_new_banned_ip->psz_ip = malloc( strlen( psz_new_banned_ip ) + 1 );
1286 if( !p_new_banned_ip->psz_ip )
1291 strcpy( p_new_banned_ip->psz_ip, psz_new_banned_ip );
1293 msg_Dbg( p_httpt, "Banning IP %s", psz_new_banned_ip );
1295 if( p_httpt->p_first_banned_ip )
1297 httpd_banned_ip_t *p_last;
1299 p_last = p_httpt->p_first_banned_ip;
1300 while( p_last->p_next )
1302 p_last = p_last->p_next;
1305 p_last->p_next = p_new_banned_ip;
1306 p_new_banned_ip->p_prev = p_last;
1310 p_new_banned_ip->p_prev = NULL;
1312 p_httpt->p_first_banned_ip = p_new_banned_ip;
1315 p_httpt->i_banned_ip_count++;
1319 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *p_httpt, char *psz_ip )
1321 httpd_banned_ip_t *p_ip;
1323 p_ip = p_httpt->p_first_banned_ip;
1327 if( strcmp( psz_ip, p_ip->psz_ip ) == 0 )
1331 p_ip = p_ip->p_next;
1337 static int httpd_UnbanIP( httpd_sys_t *p_httpt, httpd_banned_ip_t *p_banned_ip )
1344 msg_Dbg( p_httpt, "Unbanning IP %s",p_banned_ip->psz_ip);
1346 /* first cut out from list */
1347 if( p_banned_ip->p_prev )
1349 p_banned_ip->p_prev->p_next = p_banned_ip->p_next;
1353 p_httpt->p_first_banned_ip = p_banned_ip->p_next;
1356 if( p_banned_ip->p_next )
1358 p_banned_ip->p_next->p_prev = p_banned_ip->p_prev;
1361 FREE( p_banned_ip->psz_ip );
1362 FREE( p_banned_ip );
1364 p_httpt->i_banned_ip_count--;
1369 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
1371 httpd_connection_t *p_con;
1373 msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
1375 /* verify if it's a banned ip */
1376 if(httpd_GetbannedIP( p_httpt,inet_ntoa( p_sock->sin_addr ) ) )
1378 msg_Dbg( p_httpt, "Ip %s banned : closing connection", inet_ntoa( p_sock->sin_addr ) );
1383 /* create a new connection and link it */
1384 p_con = malloc( sizeof( httpd_connection_t ) );
1387 msg_Err( p_httpt, "Out of memory" );
1390 p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST;
1392 p_con->i_last_activity_date = mdate();
1394 p_con->sock = *p_sock;
1395 p_con->psz_file = NULL;
1396 p_con->i_http_error = 0;
1397 p_con->psz_user = NULL;
1398 p_con->psz_password = NULL;
1399 p_con->p_file = NULL;
1401 p_con->i_request_size = 0;
1402 p_con->p_request = NULL;
1404 p_con->i_buffer = 0;
1405 p_con->i_buffer_size = 8096;
1406 p_con->p_buffer = malloc( p_con->i_buffer_size );
1407 if( !p_con->p_buffer )
1409 msg_Err( p_httpt, "Out of memory");
1413 p_con->i_stream_pos = 0; /* updated by httpd_thread */
1414 p_con->p_next = NULL;
1416 if( p_httpt->p_first_connection )
1418 httpd_connection_t *p_last;
1420 p_last = p_httpt->p_first_connection;
1421 while( p_last->p_next )
1423 p_last = p_last->p_next;
1426 p_last->p_next = p_con;
1427 p_con->p_prev = p_last;
1431 p_con->p_prev = NULL;
1433 p_httpt->p_first_connection = p_con;
1436 p_httpt->i_connection_count++;
1439 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1441 msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1443 p_httpt->i_connection_count--;
1444 /* first cut out from list */
1447 p_con->p_prev->p_next = p_con->p_next;
1451 p_httpt->p_first_connection = p_con->p_next;
1456 p_con->p_next->p_prev = p_con->p_prev;
1459 if( p_con->p_file ) p_con->p_file->i_ref--;
1460 FREE( p_con->psz_file );
1462 FREE( p_con->p_buffer );
1463 SOCKET_CLOSE( p_con->fd );
1465 FREE( p_con->psz_user );
1466 FREE( p_con->psz_password );
1468 FREE( p_con->p_request );
1472 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1474 char *p = *pp_buffer;
1477 while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1483 for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1488 word[__MIN( i, i_word_max -1 )] = '\0';
1493 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1497 for( p = *pp_buffer; p < p_end; p++ )
1499 if( p + 1 < p_end && *p == '\n' )
1504 if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1511 return VLC_EGENERIC;
1514 /*char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/
1515 static void b64_decode( char *dest, char *src )
1520 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
1521 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
1522 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
1523 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
1524 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
1525 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
1526 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
1527 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
1528 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
1529 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
1530 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
1531 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
1532 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
1533 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
1534 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
1535 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
1538 for( i_level = 0; *src != '\0'; src++ )
1542 c = b64[(unsigned int)*src];
1555 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1559 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1563 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1572 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1578 int b_xplaystream = VLC_FALSE;
1579 /* Size is checked for all of these */
1583 char user[512] = "";
1584 char password[512] = "";
1586 msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1589 p = p_con->p_buffer;
1590 p_end = p + strlen( p ) + 1;
1592 httpd_RequestGetWord( command, 32, &p, p_end );
1593 httpd_RequestGetWord( url, 1024, &p, p_end );
1594 httpd_RequestGetWord( version, 32, &p, p_end );
1596 msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1598 p_con->p_request = NULL;
1599 p_con->i_request_size = 0;
1600 if( !strcmp( command, "GET" ) )
1602 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1604 else if( !strcmp( command, "POST" ))
1606 p_con->i_method = HTTPD_CONNECTION_METHOD_POST;
1608 else if( !strcmp( command, "HEAD" ))
1610 p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD;
1615 p_con->psz_file = strdup( "/501.html" );
1616 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1617 p_con->i_http_error = 501;
1621 if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1623 p_con->psz_file = strdup( "/505.html" );
1624 p_con->i_http_error = 505;
1634 if( httpd_RequestNextLine( &p, p_end ) )
1637 msg_Dbg( p_httpt, "failled new line" );
1642 msg_Dbg( p_httpt, "new line=%s", p );
1644 httpd_RequestGetWord( header, 1024, &p, p_end );
1645 if( !strcmp( header, "\r\n" ) || !strcmp( header, "\n" ) )
1650 if( !strcmp( header, "Authorization:" ) )
1654 httpd_RequestGetWord( method, 32, &p, p_end );
1655 if( !strcasecmp( method, "BASIC" ) )
1660 httpd_RequestGetWord( basic, 1024, &p, p_end );
1662 msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1664 b64_decode( decoded, basic );
1666 msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1668 if( strchr( decoded, ':' ) )
1670 char *p = strchr( decoded, ':' );
1673 strcpy( user, decoded );
1674 strcpy( password, p );
1678 else if( !strcmp( header, "Pragma:" ) )
1682 httpd_RequestGetWord( method, 128, &p, p_end );
1683 if( !strcasecmp( method, "xPlayStrm=1" ) )
1685 b_xplaystream = VLC_TRUE;
1690 if( strchr( url, '?' ) )
1692 char *p_request = strchr( url, '?' );
1693 *p_request++ = '\0';
1694 p_con->psz_file = strdup( url );
1695 p_con->p_request = strdup( p_request );
1696 p_con->i_request_size = strlen( p_con->p_request );
1700 p_con->psz_file = strdup( url );
1704 if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1707 if( strstr( p_con->p_buffer, "\r\n\r\n" ) )
1709 p_request = strstr( p_con->p_buffer, "\r\n\r\n" ) + 4;
1711 else if( strstr( p_con->p_buffer, "\n\n" ) )
1713 p_request = strstr( p_con->p_buffer, "\n\n" ) + 2;
1719 if( p_request && p_request < p_end )
1721 p_con->i_request_size = p_end - p_request;
1722 p_con->p_request = malloc( p_con->i_request_size + 1);
1723 if( !p_con->p_request)
1725 msg_Err( p_httpt, "Out of memory" );
1728 memcpy( p_con->p_request,
1730 p_con->i_request_size );
1732 p_con->p_request[p_con->i_request_size] = '\0';
1735 p_con->i_http_error = 200;
1739 msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1741 FREE( p_con->p_buffer );
1742 p_con->i_buffer = 0;
1743 p_con->i_buffer_size = 0;
1746 vlc_mutex_lock( &p_httpt->file_lock );
1750 p_con->p_file = NULL;
1751 for( i = 0; i < p_httpt->i_file_count; i++ )
1753 /* Our strdup call failed */
1754 if( !p_con->psz_file ) return;
1755 if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1757 if( p_httpt->file[i]->b_stream ||
1758 p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD ||
1759 ( p_con->i_method == HTTPD_CONNECTION_METHOD_GET && p_httpt->file[i]->pf_get ) ||
1760 ( p_con->i_method == HTTPD_CONNECTION_METHOD_POST && p_httpt->file[i]->pf_post ) )
1762 p_con->p_file = p_httpt->file[i];
1768 if( !p_con->p_file )
1770 p_con->psz_file = strdup( "/404.html" );
1771 p_con->i_http_error = 404;
1772 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1774 /* XXX be sure that "/404.html" exist else ... */
1777 if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1779 if( strcmp( user, p_con->p_file->psz_user ) ||
1780 strcmp( password, p_con->p_file->psz_password ) )
1782 p_con->psz_file = strdup( "/401.html" );
1783 strcpy( user, p_con->p_file->psz_user );
1784 p_con->i_http_error = 401;
1785 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1787 /* XXX do not put password on 404 else ... */
1791 if( !strcmp( p_con->p_file->psz_mime, "video/x-ms-asf-stream" ) &&
1792 p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1794 p_con->psz_file = strdup( "/400.html" );
1795 p_con->i_http_error = 400;
1796 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1801 p_con->p_file->i_ref++;
1803 vlc_mutex_unlock( &p_httpt->file_lock );
1806 switch( p_con->i_http_error )
1812 psz_status = "Bad Request";
1815 psz_status = "Authorization Required";
1818 psz_status = "Unknown";
1822 p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1824 p_con->i_buffer_size = 4096;
1825 p_con->i_buffer = 0;
1827 /* we send stream header with this one */
1828 if( p_con->i_http_error == 200 && p_con->p_file->b_stream )
1830 p_con->i_buffer_size += p_con->p_file->i_header_size;
1833 p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1836 msg_Err( p_httpt, "Out of memory" );
1839 p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1841 /* Special mmsh case cludgy but ...*/
1842 if( !strcmp( p_con->p_file->psz_mime, "video/x-ms-asf-stream" ) &&
1843 p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
1845 p += sprintf( p, "Content-type: application/octet-stream\r\n" );
1846 p += sprintf( p, "Server: Cougar 4.1.0.3921\r\n" );
1847 p += sprintf( p, "Cache-Control: no-cache\r\n" );
1848 p += sprintf( p, "Pragma: no-cache\r\n" );
1849 p += sprintf( p, "Pragma: client-id=%d\r\n", rand()&0x7fff );
1850 p += sprintf( p, "Pragma: features=\"broadcast\"\r\n" );
1852 if( !b_xplaystream )
1854 p_con->i_method = HTTPD_CONNECTION_METHOD_ASFHEAD;
1859 p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1860 p += sprintf( p, "Cache-Control: no-cache\r\n" );
1862 if( p_con->i_http_error == 401 )
1864 p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1867 if( p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD )
1869 p += sprintf( p, "Content-Length: 0\r\n" );
1871 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_ASFHEAD &&
1872 p_con->p_file->i_header_size > 0 )
1874 p += sprintf( p, "Content-Length: %d\r\n", p_con->p_file->i_header_size );
1877 p += sprintf( p, "\r\n" );
1879 p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1;
1881 if( p_con->i_http_error == 200 &&
1882 p_con->i_method != HTTPD_CONNECTION_METHOD_HEAD &&
1883 p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 )
1885 /* add stream header */
1886 memcpy( &p_con->p_buffer[p_con->i_buffer_size],
1887 p_con->p_file->p_header,
1888 p_con->p_file->i_header_size );
1889 p_con->i_buffer_size += p_con->p_file->i_header_size;
1892 if( p_con->i_method == HTTPD_CONNECTION_METHOD_ASFHEAD )
1894 p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD;
1897 msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1900 #define HTTPD_STREAM_PACKET 10000
1901 static void httpd_Thread( httpd_sys_t *p_httpt )
1903 httpd_file_t *p_page_400;
1904 httpd_file_t *p_page_401;
1905 httpd_file_t *p_page_404;
1907 httpd_connection_t *p_con;
1909 msg_Info( p_httpt, "httpd started" );
1911 p_page_400 = _RegisterFile( p_httpt,
1912 "/400.html", "text/html",
1916 (httpd_file_callback_args_t*)NULL );
1918 p_page_401 = _RegisterFile( p_httpt,
1919 "/401.html", "text/html",
1923 (httpd_file_callback_args_t*)NULL );
1924 p_page_404 = _RegisterFile( p_httpt,
1925 "/404.html", "text/html",
1929 (httpd_file_callback_args_t*)NULL );
1931 while( !p_httpt->b_die )
1933 struct timeval timeout;
1936 int i_handle_max = 0;
1939 if( p_httpt->i_host_count <= 0 )
1941 msleep( 100 * 1000 );
1945 /* we will create a socket set with host and connection */
1946 FD_ZERO( &fds_read );
1947 FD_ZERO( &fds_write );
1949 vlc_mutex_lock( &p_httpt->host_lock );
1950 vlc_mutex_lock( &p_httpt->connection_lock );
1951 for( i = 0; i < p_httpt->i_host_count; i++ )
1953 FD_SET( p_httpt->host[i]->fd, &fds_read );
1954 i_handle_max = __MAX( i_handle_max, p_httpt->host[i]->fd );
1956 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1958 /* no more than 10s of inactivity */
1959 if( p_con->i_last_activity_date + (mtime_t)HTTPD_CONNECTION_MAX_UNUSED < mdate() ||
1960 p_con->i_state == HTTPD_CONNECTION_TO_BE_CLOSED)
1962 httpd_connection_t *p_next = p_con->p_next;
1964 msg_Dbg( p_httpt, "close unused connection" );
1965 httpd_ConnnectionClose( p_httpt, p_con );
1970 if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM && p_con->i_stream_pos + HTTPD_STREAM_PACKET >= p_con->p_file->i_buffer_pos )
1972 p_con = p_con->p_next;
1976 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1978 FD_SET( p_con->fd, &fds_read );
1982 FD_SET( p_con->fd, &fds_write );
1984 i_handle_max = __MAX( i_handle_max, p_con->fd );
1986 p_con = p_con->p_next;
1988 vlc_mutex_unlock( &p_httpt->host_lock );
1989 vlc_mutex_unlock( &p_httpt->connection_lock );
1991 /* we will wait 0.5s */
1993 timeout.tv_usec = 500*1000;
1995 i_ret = select( i_handle_max + 1,
2000 if( i_ret == -1 && errno != EINTR )
2002 msg_Warn( p_httpt, "cannot select sockets" );
2012 vlc_mutex_lock( &p_httpt->host_lock );
2013 /* accept/refuse new connection */
2014 for( i = 0; i < p_httpt->i_host_count; i++ )
2016 int i_sock_size = sizeof( struct sockaddr_in );
2017 struct sockaddr_in sock;
2020 fd = accept( p_httpt->host[i]->fd, (struct sockaddr *)&sock,
2024 #if defined( WIN32 ) || defined( UNDER_CE )
2026 unsigned long i_dummy = 1;
2027 ioctlsocket( fd, FIONBIO, &i_dummy );
2030 fcntl( fd, F_SETFL, O_NONBLOCK );
2033 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
2035 msg_Warn( p_httpt, "max connection reached" );
2039 /* create a new connection and link it */
2040 httpd_ConnnectionNew( p_httpt, fd, &sock );
2044 vlc_mutex_unlock( &p_httpt->host_lock );
2046 vlc_mutex_lock( &p_httpt->file_lock );
2047 /* now do work for all connections */
2048 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
2050 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
2054 i_len = recv( p_con->fd,
2055 p_con->p_buffer + p_con->i_buffer,
2056 p_con->i_buffer_size - p_con->i_buffer, 0 );
2059 #if defined( WIN32 ) || defined( UNDER_CE )
2060 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
2062 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
2065 httpd_connection_t *p_next = p_con->p_next;
2067 httpd_ConnnectionClose( p_httpt, p_con );
2070 else if( i_len > 0 )
2073 p_con->i_last_activity_date = mdate();
2074 p_con->i_buffer += i_len;
2076 ptr = p_con->p_buffer + p_con->i_buffer;
2078 if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
2079 ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
2080 p_con->i_buffer >= p_con->i_buffer_size )
2082 p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
2083 httpd_ConnectionParseRequest( p_httpt, p_con );
2086 p_con = p_con->p_next;
2090 p_con = p_con->p_next;
2092 continue; /* just for clarity */
2094 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
2099 if( p_con->i_buffer_size - p_con->i_buffer > 0 )
2101 i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
2108 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 );
2111 #if defined( WIN32 ) || defined( UNDER_CE )
2112 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
2114 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
2117 httpd_connection_t *p_next = p_con->p_next;
2119 httpd_ConnnectionClose( p_httpt, p_con );
2122 else if( i_len > 0 )
2124 p_con->i_last_activity_date = mdate();
2125 p_con->i_buffer += i_len;
2127 if( p_con->i_buffer >= p_con->i_buffer_size )
2129 if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
2131 p_con->i_buffer_size = 0;
2132 p_con->i_buffer = 0;
2133 FREE( p_con->p_buffer );
2135 if( !p_con->p_file->b_stream || p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD )
2137 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; /* be sure to out from HTTPD_CONNECTION_SENDING_HEADER */
2138 if( p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
2140 p_con->p_file->pf_get( p_con->p_file->p_sys,
2141 p_con->p_request, p_con->i_request_size,
2142 &p_con->p_buffer, &p_con->i_buffer_size );
2144 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
2146 p_con->p_file->pf_post( p_con->p_file->p_sys,
2147 p_con->p_request, p_con->i_request_size,
2148 &p_con->p_buffer, &p_con->i_buffer_size );
2152 /* HTTPD_CONNECTION_METHOD_HEAD for example */
2153 p_con->p_buffer = NULL;
2154 p_con->i_buffer_size = 0;
2159 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
2160 p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos;
2162 p_con = p_con->p_next;
2166 httpd_connection_t *p_next = p_con->p_next;
2168 httpd_ConnnectionClose( p_httpt, p_con );
2174 p_con = p_con->p_next;
2179 p_con = p_con->p_next;
2181 continue; /* just for clarity */
2183 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
2185 httpd_stream_t *p_stream = p_con->p_file;
2189 if( p_con->i_stream_pos < p_stream->i_buffer_pos )
2192 /* check if this p_con aren't to late */
2193 if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos )
2195 fprintf( stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n",
2196 p_con->i_stream_pos, p_stream->i_buffer_pos );
2197 p_con->i_stream_pos = p_stream->i_buffer_last_pos;
2200 i_pos = p_con->i_stream_pos % p_stream->i_buffer_size;
2201 /* size until end of buffer */
2202 i_write = p_stream->i_buffer_size - i_pos;
2203 /* is it more than valid data */
2204 if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos )
2206 i_write = p_stream->i_buffer_pos - p_con->i_stream_pos;
2208 /* limit to HTTPD_STREAM_PACKET */
2209 if( i_write > HTTPD_STREAM_PACKET )
2211 i_write = HTTPD_STREAM_PACKET;
2213 i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 );
2215 #if defined( WIN32 ) || defined( UNDER_CE )
2216 if( ( i_send < 0 && WSAGetLastError() != WSAEWOULDBLOCK )|| ( i_send == 0 ) )
2218 if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )|| ( i_send == 0 ) )
2221 httpd_connection_t *p_next = p_con->p_next;
2223 httpd_ConnnectionClose( p_httpt, p_con );
2227 else if( i_send > 0 )
2229 p_con->i_last_activity_date = mdate();
2230 p_con->i_stream_pos += i_send;
2233 p_con = p_con->p_next;
2234 continue; /* just for clarity */
2236 else if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
2238 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
2239 p_con = p_con->p_next;
2241 } /* for over connection */
2243 vlc_mutex_unlock( &p_httpt->file_lock );
2246 msg_Info( p_httpt, "httpd stopped" );
2248 _UnregisterFile( p_httpt, p_page_400 );
2249 _UnregisterFile( p_httpt, p_page_401 );
2250 _UnregisterFile( p_httpt, p_page_404 );