1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: httpd.c,v 1.27 2003/08/26 00:40:27 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 * );
117 //#define httpd_stream_t httpd_file_t
118 static httpd_stream_t *RegisterStream ( httpd_t *,
119 char *psz_file, char *psz_mime,
120 char *psz_user, char *psz_password );
121 static int SendStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
122 static int HeaderStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
123 static void UnregisterStream( httpd_t *, httpd_stream_t* );
124 static int Control ( httpd_t *, int , void*, void* );
125 /*****************************************************************************
126 * Internal definitions
127 *****************************************************************************/
135 struct sockaddr_in sock;
140 enum httpd_authenticate_e
142 HTTPD_AUTHENTICATE_NONE = 0,
143 HTTPD_AUTHENTICATE_BASIC = 1
146 //typedef httpd_file_t httpd_stream_t;
156 int i_authenticate_method;
157 char *psz_user; /* NULL if no auth */
158 char *psz_password; /* NULL if no auth */
160 vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback
161 true: it's up to the program to give data to httpd */
162 void *p_sys; /* provided for user */
163 httpd_file_callback pf_get; /* it should allocate and fill *pp_data and *pi_data */
164 httpd_file_callback pf_post; /* it should allocate and fill *pp_data and *pi_data */
168 /* circular buffer for stream only */
169 int i_buffer_size; /* buffer size, can't be reallocated smaller */
170 uint8_t *p_buffer; /* buffer */
171 int64_t i_buffer_pos; /* absolute position from begining */
172 int i_buffer_last_pos; /* a new connection will start with that */
174 /* data to be send at connection time (if any) */
180 enum httpd_connection_state_e
182 HTTPD_CONNECTION_RECEIVING_REQUEST = 1,
183 HTTPD_CONNECTION_SENDING_HEADER = 2,
184 HTTPD_CONNECTION_SENDING_FILE = 3,
185 HTTPD_CONNECTION_SENDING_STREAM = 4,
186 HTTPD_CONNECTION_TO_BE_CLOSED = 5
189 enum httpd_connection_method_e
191 HTTPD_CONNECTION_METHOD_GET = 1,
192 HTTPD_CONNECTION_METHOD_POST = 2,
193 HTTPD_CONNECTION_METHOD_HEAD = 3,
194 /* cludgy, only used when parsing connection request */
195 HTTPD_CONNECTION_METHOD_ASFHEAD = 4
198 typedef struct httpd_connection_s
200 struct httpd_connection_s *p_next;
201 struct httpd_connection_s *p_prev;
203 struct sockaddr_in sock;
205 mtime_t i_last_activity_date;
208 int i_method; /* get/post */
210 char *psz_file; // file to be send
211 int i_http_error; // error to be send with the file
212 char *psz_user; // if Authorization in the request header
215 uint8_t *p_request; // whith get: ?<*>, with post: main data
218 httpd_file_t *p_file;
220 /* used while sending header and file */
223 int i_buffer; /* private */
225 /* used for stream */
226 int64_t i_stream_pos; /* absolute pos in stream */
227 } httpd_connection_t;
229 /* Linked List of banned IP */
230 typedef struct httpd_banned_ip_s
232 struct httpd_banned_ip_s *p_next;
233 struct httpd_banned_ip_s *p_prev;
245 vlc_mutex_t host_lock;
246 volatile int i_host_count;
249 vlc_mutex_t file_lock;
253 vlc_mutex_t connection_lock;
254 int i_connection_count;
255 httpd_connection_t *p_first_connection;
257 vlc_mutex_t ban_lock;
258 int i_banned_ip_count;
259 httpd_banned_ip_t *p_first_banned_ip;
262 static void httpd_Thread( httpd_sys_t *p_httpt );
263 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
264 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
265 static int httpd_UnbanIP( httpd_sys_t *, httpd_banned_ip_t *);
267 static int httpd_BanIP( httpd_sys_t *, char *);
269 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *, char * );
271 /*****************************************************************************
273 *****************************************************************************/
275 static int Open( vlc_object_t *p_this )
277 httpd_t *p_httpd = (httpd_t*)p_this;
278 httpd_sys_t *p_httpt;
280 /* Launch httpt thread */
281 if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
283 msg_Err( p_this, "out of memory" );
284 return( VLC_EGENERIC );
290 /* init httpt_t structure */
291 vlc_mutex_init( p_httpd, &p_httpt->host_lock );
292 p_httpt->i_host_count = 0;
293 p_httpt->host = NULL;
295 vlc_mutex_init( p_httpd, &p_httpt->file_lock );
296 p_httpt->i_file_count = 0;
297 p_httpt->file = NULL;
299 vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
300 p_httpt->i_connection_count = 0;
301 p_httpt->p_first_connection = NULL;
303 vlc_mutex_init( p_httpd, &p_httpt->ban_lock );
304 p_httpt->i_banned_ip_count = 0;
305 p_httpt->p_first_banned_ip = NULL;
307 /* start the thread */
308 if( vlc_thread_create( p_httpt, "httpd thread",
309 httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
311 msg_Err( p_this, "cannot spawn http thread" );
313 vlc_mutex_destroy( &p_httpt->host_lock );
314 vlc_mutex_destroy( &p_httpt->file_lock );
315 vlc_mutex_destroy( &p_httpt->connection_lock );
316 vlc_mutex_destroy( &p_httpt->ban_lock );
318 vlc_object_destroy( p_httpt );
319 return( VLC_EGENERIC );
322 msg_Info( p_httpd, "http thread launched" );
324 p_httpd->p_sys = p_httpt;
325 p_httpd->pf_register_host = RegisterHost;
326 p_httpd->pf_unregister_host = UnregisterHost;
327 p_httpd->pf_register_file = RegisterFile;
328 p_httpd->pf_unregister_file = UnregisterFile;
329 p_httpd->pf_register_stream = RegisterStream;
330 p_httpd->pf_header_stream = HeaderStream;
331 p_httpd->pf_send_stream = SendStream;
332 p_httpd->pf_unregister_stream=UnregisterStream;
333 p_httpd->pf_control = Control;
335 return( VLC_SUCCESS );
338 /*****************************************************************************
339 * Close: close the target
340 *****************************************************************************/
341 static void Close( vlc_object_t * p_this )
343 httpd_t *p_httpd = (httpd_t*)p_this;
344 httpd_sys_t *p_httpt = p_httpd->p_sys;
346 httpd_connection_t *p_con;
347 httpd_banned_ip_t *p_banned_ip;
352 vlc_thread_join( p_httpt );
354 /* first close all host */
355 vlc_mutex_destroy( &p_httpt->host_lock );
356 if( p_httpt->i_host_count )
358 msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
360 for( i = 0; i < p_httpt->i_host_count; i++ )
362 #define p_host p_httpt->host[i]
363 FREE( p_host->psz_host_addr );
364 SOCKET_CLOSE( p_host->fd );
369 FREE( p_httpt->host );
372 vlc_mutex_destroy( &p_httpt->file_lock );
373 if( p_httpt->i_file_count )
375 msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
377 for( i = 0; i < p_httpt->i_file_count; i++ )
379 #define p_file p_httpt->file[i]
380 FREE( p_file->psz_file );
381 FREE( p_file->psz_mime );
382 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
384 FREE( p_file->psz_user );
385 FREE( p_file->psz_password );
387 FREE( p_file->p_buffer );
392 FREE( p_httpt->file );
394 /* andd close all connection */
395 vlc_mutex_destroy( &p_httpt->connection_lock );
396 if( p_httpt->i_connection_count )
398 msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
400 while( ( p_con = p_httpt->p_first_connection ) )
402 httpd_ConnnectionClose( p_httpt, p_con );
405 /* Free all banned IP */
406 vlc_mutex_destroy( &p_httpt->ban_lock );
407 while( ( p_banned_ip = p_httpt->p_first_banned_ip))
409 httpd_UnbanIP(p_httpt,p_banned_ip);
412 msg_Info( p_httpd, "httpd instance closed" );
413 vlc_object_destroy( p_httpt );
417 /****************************************************************************
418 ****************************************************************************
421 ****************************************************************************
422 ****************************************************************************/
423 static int BuildAddr( struct sockaddr_in * p_socket,
424 const char * psz_address, int i_port )
427 memset( p_socket, 0, sizeof( struct sockaddr_in ) );
428 p_socket->sin_family = AF_INET; /* family */
429 p_socket->sin_port = htons( (uint16_t)i_port );
432 p_socket->sin_addr.s_addr = INADDR_ANY;
436 struct hostent * p_hostent;
438 /* Try to convert address directly from in_addr - this will work if
439 * psz_address is dotted decimal. */
440 #ifdef HAVE_ARPA_INET_H
441 if( !inet_aton( psz_address, &p_socket->sin_addr ) )
443 p_socket->sin_addr.s_addr = inet_addr( psz_address );
444 if( p_socket->sin_addr.s_addr == INADDR_NONE )
447 /* We have a fqdn, try to find its address */
448 if ( (p_hostent = gethostbyname( psz_address )) == NULL )
453 /* Copy the first address of the host in the socket address */
454 memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
455 p_hostent->h_length );
463 * listen on a host for a httpd instance
466 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
468 httpd_host_t *p_host;
469 struct sockaddr_in sock;
473 #if !defined( WIN32 ) && !defined( UNDER_CE )
477 if( BuildAddr( &sock, psz_host_addr, i_port ) )
479 msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
483 /* is it already declared ? */
484 vlc_mutex_lock( &p_httpt->host_lock );
485 for( i = 0; i < p_httpt->i_host_count; i++ )
487 if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
488 ( p_httpt->host[i]->sock.sin_addr.s_addr == INADDR_ANY ||
489 p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr ) )
495 if( i < p_httpt->i_host_count )
497 /* yes, increment ref count and succed */
498 p_httpt->host[i]->i_ref++;
499 vlc_mutex_unlock( &p_httpt->host_lock );
500 return( p_httpt->host[i] );
503 /* need to add a new listening socket */
506 fd = socket( AF_INET, SOCK_STREAM, 0 );
509 msg_Err( p_httpt, "cannot open socket" );
514 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
515 (void *) &i_opt, sizeof( i_opt ) ) < 0 )
517 msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
520 if( bind( fd, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 )
522 msg_Err( p_httpt, "cannot bind socket" );
525 /* set to non-blocking */
526 #if defined( WIN32 ) || defined( UNDER_CE )
528 unsigned long i_dummy = 1;
529 if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
531 msg_Err( p_httpt, "cannot set socket to non-blocking mode" );
536 if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
538 msg_Err( p_httpt, "cannot F_GETFL socket" );
541 if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
543 msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
548 if( listen( fd, LISTEN_BACKLOG ) < 0 )
550 msg_Err( p_httpt, "cannot listen socket" );
556 p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) );
560 p_httpt->host = malloc( sizeof( httpd_host_t *) );
562 p_host = malloc( sizeof( httpd_host_t ) );
564 p_host->psz_host_addr = strdup( psz_host_addr );
565 p_host->i_port = i_port;
569 p_httpt->host[p_httpt->i_host_count++] = p_host;
570 vlc_mutex_unlock( &p_httpt->host_lock );
575 vlc_mutex_unlock( &p_httpt->host_lock );
582 static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
584 return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
588 * remove a listening host for an httpd instance
590 static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
594 vlc_mutex_lock( &p_httpt->host_lock );
595 for( i = 0; i < p_httpt->i_host_count; i++ )
597 if( p_httpt->host[i] == p_host )
602 if( i >= p_httpt->i_host_count )
604 vlc_mutex_unlock( &p_httpt->host_lock );
605 msg_Err( p_httpt, "cannot unregister host" );
611 if( p_host->i_ref > 0 )
614 vlc_mutex_unlock( &p_httpt->host_lock );
619 FREE( p_host->psz_host_addr );
620 SOCKET_CLOSE( p_host->fd );
624 if( p_httpt->i_host_count <= 1 )
626 FREE( p_httpt->host );
627 p_httpt->i_host_count = 0;
633 i_move = p_httpt->i_host_count - i - 1;
637 memmove( &p_httpt->host[i],
639 i_move * sizeof( httpd_host_t * ) );
642 p_httpt->i_host_count--;
643 p_httpt->host = realloc( p_httpt->host,
644 p_httpt->i_host_count * sizeof( httpd_host_t * ) );
647 vlc_mutex_unlock( &p_httpt->host_lock );
649 static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
651 _UnregisterHost( p_httpd->p_sys, p_host );
655 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
658 if( p_httpt->i_file_count )
660 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) );
664 p_httpt->file = malloc( sizeof( httpd_file_t *) );
667 p_httpt->file[p_httpt->i_file_count++] = p_file;
670 static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt,
671 char *psz_file, char *psz_mime,
672 char *psz_user, char *psz_password,
673 httpd_file_callback pf_get,
674 httpd_file_callback pf_post,
675 httpd_file_callback_args_t *p_args )
677 httpd_file_t *p_file;
680 vlc_mutex_lock( &p_httpt->file_lock );
681 for( i = 0; i < p_httpt->i_file_count; i++ )
683 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
688 if( i < p_httpt->i_file_count )
690 vlc_mutex_unlock( &p_httpt->file_lock );
691 msg_Err( p_httpt, "%s already registered", psz_file );
695 p_file = malloc( sizeof( httpd_file_t ) );
697 p_file->psz_file = strdup( psz_file );
698 p_file->psz_mime = strdup( psz_mime );
699 if( psz_user && *psz_user )
701 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
702 p_file->psz_user = strdup( psz_user );
703 p_file->psz_password = strdup( psz_password );
707 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
708 p_file->psz_user = NULL;
709 p_file->psz_password = NULL;
712 p_file->b_stream = VLC_FALSE;
713 p_file->p_sys = p_args;
714 p_file->pf_get = pf_get;
715 p_file->pf_post = pf_post;
717 p_file->i_buffer_size = 0;
718 p_file->i_buffer_last_pos = 0;
719 p_file->i_buffer_pos = 0;
720 p_file->p_buffer = NULL;
722 p_file->i_header_size = 0;
723 p_file->p_header = NULL;
725 __RegisterFile( p_httpt, p_file );
727 vlc_mutex_unlock( &p_httpt->file_lock );
731 static httpd_file_t *RegisterFile( httpd_t *p_httpd,
732 char *psz_file, char *psz_mime,
733 char *psz_user, char *psz_password,
734 httpd_file_callback pf_get,
735 httpd_file_callback pf_post,
736 httpd_file_callback_args_t *p_args )
738 return( _RegisterFile( p_httpd->p_sys,
739 psz_file, psz_mime, psz_user, psz_password,
740 pf_get, pf_post, p_args ) );
743 static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt,
744 char *psz_file, char *psz_mime,
745 char *psz_user, char *psz_password )
747 httpd_stream_t *p_stream;
750 vlc_mutex_lock( &p_httpt->file_lock );
751 for( i = 0; i < p_httpt->i_file_count; i++ )
753 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
758 if( i < p_httpt->i_file_count )
760 vlc_mutex_unlock( &p_httpt->file_lock );
761 msg_Err( p_httpt, "%s already registered", psz_file );
765 p_stream = malloc( sizeof( httpd_stream_t ) );
767 p_stream->psz_file = strdup( psz_file );
768 p_stream->psz_mime = strdup( psz_mime );
769 if( psz_user && *psz_user )
771 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
772 p_stream->psz_user = strdup( psz_user );
773 p_stream->psz_password = strdup( psz_password );
777 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
778 p_stream->psz_user = NULL;
779 p_stream->psz_password = NULL;
782 p_stream->b_stream = VLC_TRUE;
783 p_stream->p_sys = NULL;
784 p_stream->pf_get = NULL;
785 p_stream->pf_post = NULL;
787 p_stream->i_buffer_size = 5*1024*1024;
788 p_stream->i_buffer_pos = 0;
789 p_stream->i_buffer_last_pos = 0;
790 p_stream->p_buffer = malloc( p_stream->i_buffer_size );
792 p_stream->i_header_size = 0;
793 p_stream->p_header = NULL;
795 __RegisterFile( p_httpt, p_stream );
797 vlc_mutex_unlock( &p_httpt->file_lock );
801 static httpd_stream_t *RegisterStream( httpd_t *p_httpd,
802 char *psz_file, char *psz_mime,
803 char *psz_user, char *psz_password )
805 return( _RegisterStream( p_httpd->p_sys,
806 psz_file, psz_mime, psz_user, psz_password ) );
809 static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
813 vlc_mutex_lock( &p_httpt->file_lock );
814 for( i = 0; i < p_httpt->i_file_count; i++ )
816 if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
821 if( i >= p_httpt->i_file_count )
823 vlc_mutex_unlock( &p_httpt->file_lock );
824 msg_Err( p_httpt, "cannot unregister file" );
828 if( p_file->i_ref > 0 )
830 httpd_connection_t *p_con;
831 /* force closing all connection for this file */
832 msg_Err( p_httpt, "closing all client connection" );
834 vlc_mutex_lock( &p_httpt->connection_lock );
835 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
837 httpd_connection_t *p_next;
839 p_next = p_con->p_next;
840 if( p_con->p_file == p_file )
842 httpd_ConnnectionClose( p_httpt, p_con );
846 vlc_mutex_unlock( &p_httpt->connection_lock );
849 FREE( p_file->psz_file );
850 FREE( p_file->psz_mime );
851 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
853 FREE( p_file->psz_user );
854 FREE( p_file->psz_password );
856 FREE( p_file->p_buffer );
857 FREE( p_file->p_header );
862 if( p_httpt->i_file_count == 1 )
864 FREE( p_httpt->file );
865 p_httpt->i_file_count = 0;
871 i_move = p_httpt->i_file_count - i - 1;
874 memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
876 p_httpt->i_file_count--;
877 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
880 vlc_mutex_unlock( &p_httpt->file_lock );
882 static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
884 _UnregisterFile( p_httpd->p_sys, p_file );
887 static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
889 _UnregisterFile( p_httpd->p_sys, p_stream );
894 static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
899 if( i_data <= 0 || p_data == NULL )
901 return( VLC_SUCCESS );
903 //fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos );
905 vlc_mutex_lock( &p_httpt->file_lock );
907 /* save this pointer (to be used by new connection) */
908 p_stream->i_buffer_last_pos = p_stream->i_buffer_pos;
910 i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size;
916 i_copy = __MIN( i_count, p_stream->i_buffer_size - i_pos );
918 memcpy( &p_stream->p_buffer[i_pos],
922 i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size;
927 p_stream->i_buffer_pos += i_data;
928 vlc_mutex_unlock( &p_httpt->file_lock );
930 return( VLC_SUCCESS );
932 static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
934 return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
937 static int HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
939 httpd_sys_t *p_httpt = p_httpd->p_sys;
941 vlc_mutex_lock( &p_httpt->file_lock );
943 FREE( p_stream->p_header );
944 if( p_data == NULL || i_data <= 0 )
946 p_stream->i_header_size = 0;
950 p_stream->i_header_size = i_data;
951 p_stream->p_header = malloc( i_data );
952 memcpy( p_stream->p_header,
956 vlc_mutex_unlock( &p_httpt->file_lock );
958 return( VLC_SUCCESS );
961 static void httpd_info_add_ss( httpd_info_t *p_info, char *name, char *value )
963 if( p_info->i_count == 0 )
965 p_info->info = malloc( sizeof( httpd_val_t ) );
970 realloc( p_info->info,
971 sizeof( httpd_val_t ) * ( p_info->i_count + 1 ) );
973 p_info->info[p_info->i_count].psz_name = strdup( name );
974 p_info->info[p_info->i_count].psz_value = strdup( value ? value : "(null)");
978 static void httpd_info_add_si( httpd_info_t *p_info, char *name, int i_value )
982 sprintf( v, "%d", i_value );
983 httpd_info_add_ss( p_info, name, v );
986 static void httpd_info_add_sp( httpd_info_t *p_info, char *name, void *value )
990 sprintf( v, "%p", value );
991 httpd_info_add_ss( p_info, name, v );
995 static int Control( httpd_t *p_httpd,
996 int i_query, void *arg1, void *arg2 )
998 httpd_sys_t *p_httpt = p_httpd->p_sys;
999 httpd_info_t *p_info;
1000 httpd_connection_t *p_con;
1006 case HTTPD_GET_HOSTS:
1008 p_info->i_count = 0;
1009 vlc_mutex_lock( &p_httpt->host_lock );
1010 for( i = 0; i < p_httpt->i_host_count; i++ )
1012 httpd_info_add_sp( p_info,
1013 "id", p_httpt->host[i] );
1014 httpd_info_add_ss( p_info,
1015 "host", p_httpt->host[i]->psz_host_addr );
1016 httpd_info_add_ss( p_info,
1018 inet_ntoa(p_httpt->host[i]->sock.sin_addr));
1019 httpd_info_add_si( p_info,
1020 "port", p_httpt->host[i]->i_port );
1022 vlc_mutex_unlock( &p_httpt->host_lock );
1024 case HTTPD_GET_URLS:
1026 p_info->i_count = 0;
1027 /* we can't take file_lock */
1028 for( i = 0; i < p_httpt->i_file_count; i++ )
1030 httpd_info_add_sp( p_info,
1031 "id", p_httpt->file[i] );
1032 httpd_info_add_si( p_info,
1033 "stream", p_httpt->file[i]->b_stream ? 1 : 0 );
1034 httpd_info_add_ss( p_info,
1035 "url", p_httpt->file[i]->psz_file );
1036 httpd_info_add_ss( p_info,
1037 "mime", p_httpt->file[i]->psz_mime );
1038 httpd_info_add_si( p_info,
1039 "protected", p_httpt->file[i]->psz_user ? 1 : 0 );
1040 httpd_info_add_si( p_info,
1041 "used", p_httpt->file[i]->i_ref );
1044 case HTTPD_GET_CONNECTIONS:
1046 p_info->i_count = 0;
1047 /* we can't take lock */
1048 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1050 if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
1052 httpd_info_add_sp( p_info,
1054 httpd_info_add_ss( p_info,
1055 "ip", inet_ntoa( p_con->sock.sin_addr ) );
1056 httpd_info_add_ss( p_info,
1057 "url", p_con->psz_file );
1058 httpd_info_add_si( p_info,
1059 "status", p_con->i_http_error );
1065 p_info->i_count = 0;
1066 return VLC_EGENERIC;
1068 case HTTPD_SET_CLOSE:
1069 sscanf( arg1, "%p", &id );
1070 fprintf( stderr, "Control: HTTPD_SET_CLOSE: id=%p", id );
1072 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1074 if( (void*)p_con == id )
1076 /* XXX don't free p_con as it could be the one that it is sending ... */
1077 p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1081 return VLC_EGENERIC;
1084 sscanf( arg1, "%p", &id );
1085 fprintf( stderr, "Control: %p", id );
1087 return VLC_EGENERIC;
1091 /****************************************************************************/
1092 /****************************************************************************/
1093 /****************************************************************************/
1094 /****************************************************************************/
1095 /****************************************************************************/
1097 static int httpd_page_400_get( httpd_file_callback_args_t *p_args,
1098 uint8_t *p_request, int i_request,
1099 uint8_t **pp_data, int *pi_data )
1103 p = *pp_data = malloc( 1024 );
1105 p += sprintf( p, "<html>\n" );
1106 p += sprintf( p, "<head>\n" );
1107 p += sprintf( p, "<title>Error 400</title>\n" );
1108 p += sprintf( p, "</head>\n" );
1109 p += sprintf( p, "<body>\n" );
1110 p += sprintf( p, "<h1><center> 400 Bad Request</center></h1>\n" );
1111 p += sprintf( p, "<hr />\n" );
1112 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1113 p += sprintf( p, "</body>\n" );
1114 p += sprintf( p, "</html>\n" );
1116 *pi_data = strlen( *pp_data ) + 1;
1121 static int httpd_page_401_get( httpd_file_callback_args_t *p_args,
1122 uint8_t *p_request, int i_request,
1123 uint8_t **pp_data, int *pi_data )
1127 p = *pp_data = malloc( 1024 );
1129 p += sprintf( p, "<html>\n" );
1130 p += sprintf( p, "<head>\n" );
1131 p += sprintf( p, "<title>Error 401</title>\n" );
1132 p += sprintf( p, "</head>\n" );
1133 p += sprintf( p, "<body>\n" );
1134 p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
1135 p += sprintf( p, "<hr />\n" );
1136 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1137 p += sprintf( p, "</body>\n" );
1138 p += sprintf( p, "</html>\n" );
1140 *pi_data = strlen( *pp_data ) + 1;
1144 static int httpd_page_404_get( httpd_file_callback_args_t *p_args,
1145 uint8_t *p_request, int i_request,
1146 uint8_t **pp_data, int *pi_data )
1150 p = *pp_data = malloc( 1024 );
1152 p += sprintf( p, "<html>\n" );
1153 p += sprintf( p, "<head>\n" );
1154 p += sprintf( p, "<title>Error 404</title>\n" );
1155 p += sprintf( p, "</head>\n" );
1156 p += sprintf( p, "<body>\n" );
1157 p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
1158 p += sprintf( p, "<hr />\n" );
1159 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1160 p += sprintf( p, "</body>\n" );
1161 p += sprintf( p, "</html>\n" );
1163 *pi_data = strlen( *pp_data ) + 1;
1169 static int httpd_BanIP( httpd_sys_t *p_httpt, char * psz_new_banned_ip)
1171 httpd_banned_ip_t *p_new_banned_ip ;
1173 p_new_banned_ip = malloc( sizeof( httpd_banned_ip_t ) );
1174 if( !p_new_banned_ip )
1178 p_new_banned_ip->p_next=NULL;
1179 p_new_banned_ip->psz_ip = malloc( strlen( psz_new_banned_ip ) + 1 );
1180 if( !p_new_banned_ip->psz_ip )
1185 strcpy( p_new_banned_ip->psz_ip, psz_new_banned_ip );
1187 msg_Dbg( p_httpt, "Banning IP %s", psz_new_banned_ip );
1189 if( p_httpt->p_first_banned_ip )
1191 httpd_banned_ip_t *p_last;
1193 p_last = p_httpt->p_first_banned_ip;
1194 while( p_last->p_next )
1196 p_last = p_last->p_next;
1199 p_last->p_next = p_new_banned_ip;
1200 p_new_banned_ip->p_prev = p_last;
1204 p_new_banned_ip->p_prev = NULL;
1206 p_httpt->p_first_banned_ip = p_new_banned_ip;
1209 p_httpt->i_banned_ip_count++;
1213 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *p_httpt, char *psz_ip )
1215 httpd_banned_ip_t *p_ip;
1217 p_ip = p_httpt->p_first_banned_ip;
1221 if( strcmp( psz_ip, p_ip->psz_ip ) == 0 )
1225 p_ip = p_ip->p_next;
1231 static int httpd_UnbanIP( httpd_sys_t *p_httpt, httpd_banned_ip_t *p_banned_ip )
1238 msg_Dbg( p_httpt, "Unbanning IP %s",p_banned_ip->psz_ip);
1240 /* first cut out from list */
1241 if( p_banned_ip->p_prev )
1243 p_banned_ip->p_prev->p_next = p_banned_ip->p_next;
1247 p_httpt->p_first_banned_ip = p_banned_ip->p_next;
1250 if( p_banned_ip->p_next )
1252 p_banned_ip->p_next->p_prev = p_banned_ip->p_prev;
1255 FREE( p_banned_ip->psz_ip );
1256 FREE( p_banned_ip );
1258 p_httpt->i_banned_ip_count--;
1263 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
1265 httpd_connection_t *p_con;
1267 msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
1269 /* verify if it's a banned ip */
1270 if(httpd_GetbannedIP( p_httpt,inet_ntoa( p_sock->sin_addr ) ) )
1272 msg_Dbg( p_httpt, "Ip %s banned : closing connection", inet_ntoa( p_sock->sin_addr ) );
1277 /* create a new connection and link it */
1278 p_con = malloc( sizeof( httpd_connection_t ) );
1279 p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST;
1281 p_con->i_last_activity_date = mdate();
1283 p_con->sock = *p_sock;
1284 p_con->psz_file = NULL;
1285 p_con->i_http_error = 0;
1286 p_con->psz_user = NULL;
1287 p_con->psz_password = NULL;
1288 p_con->p_file = NULL;
1290 p_con->i_request_size = 0;
1291 p_con->p_request = NULL;
1293 p_con->i_buffer = 0;
1294 p_con->i_buffer_size = 8096;
1295 p_con->p_buffer = malloc( p_con->i_buffer_size );
1297 p_con->i_stream_pos = 0; // updated by httpd_thread */
1298 p_con->p_next = NULL;
1300 if( p_httpt->p_first_connection )
1302 httpd_connection_t *p_last;
1304 p_last = p_httpt->p_first_connection;
1305 while( p_last->p_next )
1307 p_last = p_last->p_next;
1310 p_last->p_next = p_con;
1311 p_con->p_prev = p_last;
1315 p_con->p_prev = NULL;
1317 p_httpt->p_first_connection = p_con;
1320 p_httpt->i_connection_count++;
1323 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1325 msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1327 p_httpt->i_connection_count--;
1328 /* first cut out from list */
1331 p_con->p_prev->p_next = p_con->p_next;
1335 p_httpt->p_first_connection = p_con->p_next;
1340 p_con->p_next->p_prev = p_con->p_prev;
1343 if( p_con->p_file ) p_con->p_file->i_ref--;
1344 FREE( p_con->psz_file );
1346 FREE( p_con->p_buffer );
1347 SOCKET_CLOSE( p_con->fd );
1349 FREE( p_con->psz_user );
1350 FREE( p_con->psz_password );
1352 FREE( p_con->p_request );
1356 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1358 char *p = *pp_buffer;
1361 while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1367 for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1372 word[__MIN( i, i_word_max -1 )] = '\0';
1377 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1381 for( p = *pp_buffer; p < p_end; p++ )
1383 if( p + 1 < p_end && *p == '\n' )
1388 if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1395 return VLC_EGENERIC;
1398 //char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1399 static void b64_decode( char *dest, char *src )
1404 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
1405 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
1406 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
1407 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
1408 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
1409 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
1410 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
1411 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
1412 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
1413 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
1414 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
1415 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
1416 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
1417 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
1418 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
1419 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
1422 for( i_level = 0; *src != '\0'; src++ )
1426 c = b64[(unsigned int)*src];
1439 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1443 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1447 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1456 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1462 int b_xplaystream = VLC_FALSE;
1466 char user[512] = "";
1467 char password[512] = "";
1469 msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1472 p = p_con->p_buffer;
1473 p_end = p + strlen( p ) + 1;
1475 httpd_RequestGetWord( command, 32, &p, p_end );
1476 httpd_RequestGetWord( url, 1024, &p, p_end );
1477 httpd_RequestGetWord( version, 32, &p, p_end );
1478 //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1480 p_con->p_request = NULL;
1481 p_con->i_request_size = 0;
1482 if( !strcmp( command, "GET" ) )
1484 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1486 else if( !strcmp( command, "POST" ))
1488 p_con->i_method = HTTPD_CONNECTION_METHOD_POST;
1490 else if( !strcmp( command, "HEAD" ))
1492 p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD;
1497 p_con->psz_file = strdup( "/501.html" );
1498 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1499 p_con->i_http_error = 501;
1503 if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1505 p_con->psz_file = strdup( "/505.html" );
1506 p_con->i_http_error = 505;
1516 if( httpd_RequestNextLine( &p, p_end ) )
1518 //msg_Dbg( p_httpt, "failled new line" );
1521 //msg_Dbg( p_httpt, "new line=%s", p );
1523 httpd_RequestGetWord( header, 1024, &p, p_end );
1524 if( !strcmp( header, "\r\n" ) || !strcmp( header, "\n" ) )
1529 if( !strcmp( header, "Authorization:" ) )
1533 httpd_RequestGetWord( method, 32, &p, p_end );
1534 if( !strcasecmp( method, "BASIC" ) )
1539 httpd_RequestGetWord( basic, 1024, &p, p_end );
1540 // msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1541 b64_decode( decoded, basic );
1542 // msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1543 if( strchr( decoded, ':' ) )
1545 char *p = strchr( decoded, ':' );
1548 strcpy( user, decoded );
1549 strcpy( password, p );
1553 else if( !strcmp( header, "Pragma:" ) )
1557 httpd_RequestGetWord( method, 128, &p, p_end );
1558 if( !strcasecmp( method, "xPlayStrm=1" ) )
1560 b_xplaystream = VLC_TRUE;
1565 if( strchr( url, '?' ) )
1567 char *p_request = strchr( url, '?' );
1568 *p_request++ = '\0';
1569 p_con->psz_file = strdup( url );
1570 p_con->p_request = strdup( p_request );
1571 p_con->i_request_size = strlen( p_con->p_request );
1575 p_con->psz_file = strdup( url );
1579 if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1582 if( strstr( p_con->p_buffer, "\r\n\r\n" ) )
1584 p_request = strstr( p_con->p_buffer, "\r\n\r\n" ) + 4;
1586 else if( strstr( p_con->p_buffer, "\n\n" ) )
1588 p_request = strstr( p_con->p_buffer, "\n\n" ) + 2;
1594 if( p_request && p_request < p_end )
1596 p_con->i_request_size = p_end - p_request;
1597 p_con->p_request = malloc( p_con->i_request_size + 1);
1599 memcpy( p_con->p_request,
1601 p_con->i_request_size );
1603 p_con->p_request[p_con->i_request_size] = '\0';
1606 p_con->i_http_error = 200;
1609 //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1610 FREE( p_con->p_buffer );
1611 p_con->i_buffer = 0;
1612 p_con->i_buffer_size = 0;
1614 //vlc_mutex_lock( &p_httpt->file_lock );
1617 p_con->p_file = NULL;
1618 for( i = 0; i < p_httpt->i_file_count; i++ )
1620 if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1622 if( p_httpt->file[i]->b_stream ||
1623 p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD ||
1624 ( p_con->i_method == HTTPD_CONNECTION_METHOD_GET && p_httpt->file[i]->pf_get ) ||
1625 ( p_con->i_method == HTTPD_CONNECTION_METHOD_POST && p_httpt->file[i]->pf_post ) )
1627 p_con->p_file = p_httpt->file[i];
1633 if( !p_con->p_file )
1635 p_con->psz_file = strdup( "/404.html" );
1636 p_con->i_http_error = 404;
1637 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1639 /* XXX be sure that "/404.html" exist else ... */
1642 if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1644 if( strcmp( user, p_con->p_file->psz_user ) ||
1645 strcmp( password, p_con->p_file->psz_password ) )
1647 p_con->psz_file = strdup( "/401.html" );
1648 strcpy( user, p_con->p_file->psz_user );
1649 p_con->i_http_error = 401;
1650 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1652 /* XXX do not put password on 404 else ... */
1656 if( !strcmp( p_con->p_file->psz_mime, "video/x-ms-asf-stream" ) &&
1657 p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1659 p_con->psz_file = strdup( "/400.html" );
1660 p_con->i_http_error = 400;
1661 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1666 p_con->p_file->i_ref++;
1667 // vlc_mutex_unlock( &p_httpt->file_lock );
1669 switch( p_con->i_http_error )
1675 psz_status = "Bad Request";
1678 psz_status = "Authorization Required";
1681 psz_status = "Unknown";
1685 p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1687 p_con->i_buffer_size = 4096;
1688 p_con->i_buffer = 0;
1690 /* we send stream header with this one */
1691 if( p_con->i_http_error == 200 && p_con->p_file->b_stream )
1693 p_con->i_buffer_size += p_con->p_file->i_header_size;
1696 p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1698 p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1700 /* Special mmsh case cludgy but ...*/
1701 if( !strcmp( p_con->p_file->psz_mime, "video/x-ms-asf-stream" ) &&
1702 p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
1704 p += sprintf( p, "Content-type: application/octet-stream\r\n" );
1705 p += sprintf( p, "Server: Cougar 4.1.0.3921\r\n" );
1706 p += sprintf( p, "Cache-Control: no-cache\r\n" );
1707 p += sprintf( p, "Pragma: no-cache\r\n" );
1708 p += sprintf( p, "Pragma: client-id=%d\r\n", rand()&0x7fff );
1709 p += sprintf( p, "Pragma: features=\"broadcast\"\r\n" );
1711 if( !b_xplaystream )
1713 p_con->i_method = HTTPD_CONNECTION_METHOD_ASFHEAD;
1718 p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1719 p += sprintf( p, "Cache-Control: no-cache\r\n" );
1721 if( p_con->i_http_error == 401 )
1723 p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1726 if( p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD )
1728 p += sprintf( p, "Content-Length: 0\r\n" );
1730 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_ASFHEAD &&
1731 p_con->p_file->i_header_size > 0 )
1733 p += sprintf( p, "Content-Length: %d\r\n", p_con->p_file->i_header_size );
1736 p += sprintf( p, "\r\n" );
1738 p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1;
1740 if( p_con->i_http_error == 200 &&
1741 p_con->i_method != HTTPD_CONNECTION_METHOD_HEAD &&
1742 p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 )
1744 /* add stream header */
1745 memcpy( &p_con->p_buffer[p_con->i_buffer_size],
1746 p_con->p_file->p_header,
1747 p_con->p_file->i_header_size );
1748 p_con->i_buffer_size += p_con->p_file->i_header_size;
1751 if( p_con->i_method == HTTPD_CONNECTION_METHOD_ASFHEAD )
1753 p_con->i_method = HTTPD_CONNECTION_METHOD_HEAD;
1755 //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1757 #define HTTPD_STREAM_PACKET 10000
1758 static void httpd_Thread( httpd_sys_t *p_httpt )
1760 httpd_file_t *p_page_400;
1761 httpd_file_t *p_page_401;
1762 httpd_file_t *p_page_404;
1764 httpd_connection_t *p_con;
1766 msg_Info( p_httpt, "httpd started" );
1768 p_page_400 = _RegisterFile( p_httpt,
1769 "/400.html", "text/html",
1773 (httpd_file_callback_args_t*)NULL );
1775 p_page_401 = _RegisterFile( p_httpt,
1776 "/401.html", "text/html",
1780 (httpd_file_callback_args_t*)NULL );
1781 p_page_404 = _RegisterFile( p_httpt,
1782 "/404.html", "text/html",
1786 (httpd_file_callback_args_t*)NULL );
1788 while( !p_httpt->b_die )
1790 struct timeval timeout;
1793 int i_handle_max = 0;
1796 if( p_httpt->i_host_count <= 0 )
1798 msleep( 100 * 1000 );
1802 /* we will create a socket set with host and connection */
1803 FD_ZERO( &fds_read );
1804 FD_ZERO( &fds_write );
1806 vlc_mutex_lock( &p_httpt->host_lock );
1807 vlc_mutex_lock( &p_httpt->connection_lock );
1808 for( i = 0; i < p_httpt->i_host_count; i++ )
1810 FD_SET( p_httpt->host[i]->fd, &fds_read );
1811 i_handle_max = __MAX( i_handle_max, p_httpt->host[i]->fd );
1813 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1815 /* no more than 10s of inactivity */
1816 if( p_con->i_last_activity_date + (mtime_t)HTTPD_CONNECTION_MAX_UNUSED < mdate() ||
1817 p_con->i_state == HTTPD_CONNECTION_TO_BE_CLOSED)
1819 httpd_connection_t *p_next = p_con->p_next;
1821 msg_Dbg( p_httpt, "close unused connection" );
1822 httpd_ConnnectionClose( p_httpt, p_con );
1827 if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM && p_con->i_stream_pos + HTTPD_STREAM_PACKET >= p_con->p_file->i_buffer_pos )
1829 p_con = p_con->p_next;
1833 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1835 FD_SET( p_con->fd, &fds_read );
1839 FD_SET( p_con->fd, &fds_write );
1841 i_handle_max = __MAX( i_handle_max, p_con->fd );
1843 p_con = p_con->p_next;
1845 vlc_mutex_unlock( &p_httpt->host_lock );
1846 vlc_mutex_unlock( &p_httpt->connection_lock );
1848 /* we will wait 0.5s */
1850 timeout.tv_usec = 500*1000;
1852 i_ret = select( i_handle_max + 1,
1857 if( i_ret == -1 && errno != EINTR )
1859 msg_Warn( p_httpt, "cannot select sockets" );
1869 vlc_mutex_lock( &p_httpt->host_lock );
1870 /* accept/refuse new connection */
1871 for( i = 0; i < p_httpt->i_host_count; i++ )
1873 int i_sock_size = sizeof( struct sockaddr_in );
1874 struct sockaddr_in sock;
1877 fd = accept( p_httpt->host[i]->fd, (struct sockaddr *)&sock,
1881 #if defined( WIN32 ) || defined( UNDER_CE )
1883 unsigned long i_dummy = 1;
1884 ioctlsocket( fd, FIONBIO, &i_dummy );
1887 fcntl( fd, F_SETFL, O_NONBLOCK );
1890 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
1892 msg_Warn( p_httpt, "max connection reached" );
1896 /* create a new connection and link it */
1897 httpd_ConnnectionNew( p_httpt, fd, &sock );
1901 vlc_mutex_unlock( &p_httpt->host_lock );
1903 vlc_mutex_lock( &p_httpt->file_lock );
1904 /* now do work for all connections */
1905 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1907 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1911 i_len = recv( p_con->fd,
1912 p_con->p_buffer + p_con->i_buffer,
1913 p_con->i_buffer_size - p_con->i_buffer, 0 );
1916 #if defined( WIN32 ) || defined( UNDER_CE )
1917 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1919 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1922 httpd_connection_t *p_next = p_con->p_next;
1924 httpd_ConnnectionClose( p_httpt, p_con );
1927 else if( i_len > 0 )
1930 p_con->i_last_activity_date = mdate();
1931 p_con->i_buffer += i_len;
1933 ptr = p_con->p_buffer + p_con->i_buffer;
1935 if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
1936 ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
1937 p_con->i_buffer >= p_con->i_buffer_size )
1939 p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
1940 httpd_ConnectionParseRequest( p_httpt, p_con );
1943 p_con = p_con->p_next;
1947 p_con = p_con->p_next;
1949 continue; /* just for clarity */
1951 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
1956 if( p_con->i_buffer_size - p_con->i_buffer > 0 )
1958 i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
1964 // 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 );
1966 #if defined( WIN32 ) || defined( UNDER_CE )
1967 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1969 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1972 httpd_connection_t *p_next = p_con->p_next;
1974 httpd_ConnnectionClose( p_httpt, p_con );
1977 else if( i_len > 0 )
1979 p_con->i_last_activity_date = mdate();
1980 p_con->i_buffer += i_len;
1982 if( p_con->i_buffer >= p_con->i_buffer_size )
1984 if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
1986 p_con->i_buffer_size = 0;
1987 p_con->i_buffer = 0;
1988 FREE( p_con->p_buffer );
1990 if( !p_con->p_file->b_stream || p_con->i_method == HTTPD_CONNECTION_METHOD_HEAD )
1992 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
1993 if( p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
1995 p_con->p_file->pf_get( p_con->p_file->p_sys,
1996 p_con->p_request, p_con->i_request_size,
1997 &p_con->p_buffer, &p_con->i_buffer_size );
1999 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
2001 p_con->p_file->pf_post( p_con->p_file->p_sys,
2002 p_con->p_request, p_con->i_request_size,
2003 &p_con->p_buffer, &p_con->i_buffer_size );
2007 /* HTTPD_CONNECTION_METHOD_HEAD for example */
2008 p_con->p_buffer = NULL;
2009 p_con->i_buffer_size = 0;
2014 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
2015 p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos;
2017 p_con = p_con->p_next;
2021 httpd_connection_t *p_next = p_con->p_next;
2023 httpd_ConnnectionClose( p_httpt, p_con );
2029 p_con = p_con->p_next;
2034 p_con = p_con->p_next;
2036 continue; /* just for clarity */
2038 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
2040 httpd_stream_t *p_stream = p_con->p_file;
2044 if( p_con->i_stream_pos < p_stream->i_buffer_pos )
2047 /* check if this p_con aren't to late */
2048 if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos )
2050 fprintf( stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n",
2051 p_con->i_stream_pos, p_stream->i_buffer_pos );
2052 p_con->i_stream_pos = p_stream->i_buffer_last_pos;
2055 i_pos = p_con->i_stream_pos % p_stream->i_buffer_size;
2056 /* size until end of buffer */
2057 i_write = p_stream->i_buffer_size - i_pos;
2058 /* is it more than valid data */
2059 if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos )
2061 i_write = p_stream->i_buffer_pos - p_con->i_stream_pos;
2063 /* limit to HTTPD_STREAM_PACKET */
2064 if( i_write > HTTPD_STREAM_PACKET )
2066 i_write = HTTPD_STREAM_PACKET;
2068 i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 );
2070 #if defined( WIN32 ) || defined( UNDER_CE )
2071 if( ( i_send < 0 && WSAGetLastError() != WSAEWOULDBLOCK )|| ( i_send == 0 ) )
2073 if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )|| ( i_send == 0 ) )
2076 httpd_connection_t *p_next = p_con->p_next;
2078 httpd_ConnnectionClose( p_httpt, p_con );
2082 else if( i_send > 0 )
2084 p_con->i_last_activity_date = mdate();
2085 p_con->i_stream_pos += i_send;
2088 p_con = p_con->p_next;
2089 continue; /* just for clarity */
2091 else if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
2093 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
2094 p_con = p_con->p_next;
2096 } /* for over connection */
2098 vlc_mutex_unlock( &p_httpt->file_lock );
2101 msg_Info( p_httpt, "httpd stopped" );
2103 _UnregisterFile( p_httpt, p_page_400 );
2104 _UnregisterFile( p_httpt, p_page_401 );
2105 _UnregisterFile( p_httpt, p_page_404 );