1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001-2003 VideoLAN
5 * $Id: httpd.c,v 1.17 2003/07/01 08:30:49 adn 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 /*****************************************************************************
26 *****************************************************************************/
29 #include <sys/types.h>
39 #ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
47 #if defined( UNDER_CE )
49 #elif defined( WIN32 )
50 # include <winsock2.h>
51 # include <ws2tcpip.h>
53 # define IN_MULTICAST(a) IN_CLASSD(a)
56 # include <netdb.h> /* hostent ... */
57 # include <sys/socket.h>
58 # include <netinet/in.h>
59 # ifdef HAVE_ARPA_INET_H
60 # include <arpa/inet.h> /* inet_ntoa(), inet_aton() */
67 # define INADDR_ANY 0x00000000
70 # define INADDR_NONE 0xFFFFFFFF
73 #define LISTEN_BACKLOG 100
74 #define HTTPD_MAX_CONNECTION 512
75 #define HTTPD_CONNECTION_MAX_UNUSED 10000000
77 #define FREE( p ) if( p ) { free( p); (p) = NULL; }
79 #if defined( WIN32 ) || defined( UNDER_CE )
80 #define SOCKET_CLOSE(a) closesocket(a)
82 #define SOCKET_CLOSE(a) close(a)
85 /*****************************************************************************
87 *****************************************************************************/
88 static int Open ( vlc_object_t * );
89 static void Close ( vlc_object_t * );
91 /*****************************************************************************
93 *****************************************************************************/
95 set_description( _("HTTP 1.0 daemon") );
96 set_capability( "httpd", 42 );
97 set_callbacks( Open, Close );
100 /*****************************************************************************
102 *****************************************************************************/
103 static httpd_host_t *RegisterHost ( httpd_t *, char *, int );
104 static void UnregisterHost ( httpd_t *, httpd_host_t * );
106 static httpd_file_t *RegisterFile ( httpd_t *,
107 char *psz_file, char *psz_mime,
108 char *psz_user, char *psz_password,
109 httpd_file_callback pf_get,
110 httpd_file_callback pf_post,
111 httpd_file_callback_args_t *p_args );
112 static void UnregisterFile ( httpd_t *, httpd_file_t * );
114 //#define httpd_stream_t httpd_file_t
115 static httpd_stream_t *RegisterStream ( httpd_t *,
116 char *psz_file, char *psz_mime,
117 char *psz_user, char *psz_password );
118 static int SendStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
119 static int HeaderStream ( httpd_t *, httpd_stream_t *, uint8_t *, int );
120 static void UnregisterStream( httpd_t *, httpd_stream_t* );
122 /*****************************************************************************
123 * Internal definitions
124 *****************************************************************************/
132 struct sockaddr_in sock;
137 #define HTTPD_AUTHENTICATE_NONE 0
138 #define HTTPD_AUTHENTICATE_BASIC 1
140 //typedef httpd_file_t httpd_stream_t;
150 int i_authenticate_method;
151 char *psz_user; /* NULL if no auth */
152 char *psz_password; /* NULL if no auth */
154 vlc_bool_t b_stream; /* if false: httpd will retreive data by a callback
155 true: it's up to the program to give data to httpd */
156 void *p_sys; /* provided for user */
157 httpd_file_callback pf_get; /* it should allocate and fill *pp_data and *pi_data */
158 httpd_file_callback pf_post; /* it should allocate and fill *pp_data and *pi_data */
162 /* circular buffer for stream only */
163 int i_buffer_size; /* buffer size, can't be reallocated smaller */
164 uint8_t *p_buffer; /* buffer */
165 int64_t i_buffer_pos; /* absolute position from begining */
166 int i_buffer_last_pos; /* a new connection will start with that */
168 /* data to be send at connection time (if any) */
174 #define HTTPD_CONNECTION_RECEIVING_REQUEST 1
175 #define HTTPD_CONNECTION_SENDING_HEADER 2
176 #define HTTPD_CONNECTION_SENDING_FILE 3
177 #define HTTPD_CONNECTION_SENDING_STREAM 4
178 #define HTTPD_CONNECTION_TO_BE_CLOSED 5
180 #define HTTPD_CONNECTION_METHOD_GET 1
181 #define HTTPD_CONNECTION_METHOD_POST 2
182 typedef struct httpd_connection_s
184 struct httpd_connection_s *p_next;
185 struct httpd_connection_s *p_prev;
187 struct sockaddr_in sock;
189 mtime_t i_last_activity_date;
192 int i_method; /* get/post */
194 char *psz_file; // file to be send
195 int i_http_error; // error to be send with the file
196 char *psz_user; // if Authorization in the request header
199 uint8_t *p_request; // whith get: ?<*>, with post: main data
202 httpd_file_t *p_file;
204 /* used while sending header and file */
207 int i_buffer; /* private */
209 /* used for stream */
210 int64_t i_stream_pos; /* absolute pos in stream */
211 } httpd_connection_t;
213 /* Linked List of banned IP */
214 typedef struct httpd_banned_ip_s
216 struct httpd_banned_ip_s *p_next;
217 struct httpd_banned_ip_s *p_prev;
229 vlc_mutex_t host_lock;
230 volatile int i_host_count;
233 vlc_mutex_t file_lock;
237 vlc_mutex_t connection_lock;
238 int i_connection_count;
239 httpd_connection_t *p_first_connection;
241 vlc_mutex_t ban_lock;
242 int i_banned_ip_count;
243 httpd_banned_ip_t *p_first_banned_ip;
246 static void httpd_Thread( httpd_sys_t *p_httpt );
247 static void httpd_ConnnectionNew( httpd_sys_t *, int , struct sockaddr_in * );
248 static void httpd_ConnnectionClose( httpd_sys_t *, httpd_connection_t * );
249 static int httpd_UnbanIP( httpd_sys_t *, httpd_banned_ip_t *);
250 static int httpd_BanIP( httpd_sys_t *, char *);
251 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *, char * );
252 /*****************************************************************************
254 *****************************************************************************/
256 static int Open( vlc_object_t *p_this )
258 httpd_t *p_httpd = (httpd_t*)p_this;
259 httpd_sys_t *p_httpt;
261 /* Launch httpt thread */
262 if( !( p_httpt = vlc_object_create( p_this, sizeof( httpd_sys_t ) ) ) )
264 msg_Err( p_this, "out of memory" );
265 return( VLC_EGENERIC );
271 /* init httpt_t structure */
272 vlc_mutex_init( p_httpd, &p_httpt->host_lock );
273 p_httpt->i_host_count = 0;
274 p_httpt->host = NULL;
276 vlc_mutex_init( p_httpd, &p_httpt->file_lock );
277 p_httpt->i_file_count = 0;
278 p_httpt->file = NULL;
280 vlc_mutex_init( p_httpd, &p_httpt->connection_lock );
281 p_httpt->i_connection_count = 0;
282 p_httpt->p_first_connection = NULL;
284 vlc_mutex_init( p_httpd, &p_httpt->ban_lock );
285 p_httpt->i_banned_ip_count = 0;
286 p_httpt->p_first_banned_ip = NULL;
288 /* start the thread */
289 if( vlc_thread_create( p_httpt, "httpd thread",
290 httpd_Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
292 msg_Err( p_this, "cannot spawn http thread" );
294 vlc_mutex_destroy( &p_httpt->host_lock );
295 vlc_mutex_destroy( &p_httpt->file_lock );
296 vlc_mutex_destroy( &p_httpt->connection_lock );
297 vlc_mutex_destroy( &p_httpt->ban_lock );
299 vlc_object_destroy( p_httpt );
300 return( VLC_EGENERIC );
303 msg_Info( p_httpd, "http thread launched" );
305 p_httpd->p_sys = p_httpt;
306 p_httpd->pf_register_host = RegisterHost;
307 p_httpd->pf_unregister_host = UnregisterHost;
308 p_httpd->pf_register_file = RegisterFile;
309 p_httpd->pf_unregister_file = UnregisterFile;
310 p_httpd->pf_register_stream = RegisterStream;
311 p_httpd->pf_header_stream = HeaderStream;
312 p_httpd->pf_send_stream = SendStream;
313 p_httpd->pf_unregister_stream=UnregisterStream;
315 return( VLC_SUCCESS );
318 /*****************************************************************************
319 * Close: close the target
320 *****************************************************************************/
321 static void Close( vlc_object_t * p_this )
323 httpd_t *p_httpd = (httpd_t*)p_this;
324 httpd_sys_t *p_httpt = p_httpd->p_sys;
326 httpd_connection_t *p_con;
327 httpd_banned_ip_t *p_banned_ip;
332 vlc_thread_join( p_httpt );
334 /* first close all host */
335 vlc_mutex_destroy( &p_httpt->host_lock );
336 if( p_httpt->i_host_count )
338 msg_Err( p_httpd, "still have %d hosts registered !", p_httpt->i_host_count );
340 for( i = 0; i < p_httpt->i_host_count; i++ )
342 #define p_host p_httpt->host[i]
343 FREE( p_host->psz_host_addr );
344 SOCKET_CLOSE( p_host->fd );
349 FREE( p_httpt->host );
352 vlc_mutex_destroy( &p_httpt->file_lock );
353 if( p_httpt->i_file_count )
355 msg_Err( p_httpd, "still have %d files registered !", p_httpt->i_file_count );
357 for( i = 0; i < p_httpt->i_file_count; i++ )
359 #define p_file p_httpt->file[i]
360 FREE( p_file->psz_file );
361 FREE( p_file->psz_mime );
362 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
364 FREE( p_file->psz_user );
365 FREE( p_file->psz_password );
367 FREE( p_file->p_buffer );
372 FREE( p_httpt->file );
374 /* andd close all connection */
375 vlc_mutex_destroy( &p_httpt->connection_lock );
376 if( p_httpt->i_connection_count )
378 msg_Warn( p_httpd, "%d connections still in use", p_httpt->i_connection_count );
380 while( ( p_con = p_httpt->p_first_connection ) )
382 httpd_ConnnectionClose( p_httpt, p_con );
385 /* Free all banned IP */
386 vlc_mutex_destroy( &p_httpt->ban_lock );
387 while( ( p_banned_ip = p_httpt->p_first_banned_ip))
389 httpd_UnbanIP(p_httpt,p_banned_ip);
392 msg_Info( p_httpd, "httpd instance closed" );
393 vlc_object_destroy( p_httpt );
397 /****************************************************************************
398 ****************************************************************************
401 ****************************************************************************
402 ****************************************************************************/
403 static int BuildAddr( struct sockaddr_in * p_socket,
404 const char * psz_address, int i_port )
407 memset( p_socket, 0, sizeof( struct sockaddr_in ) );
408 p_socket->sin_family = AF_INET; /* family */
409 p_socket->sin_port = htons( (uint16_t)i_port );
412 p_socket->sin_addr.s_addr = INADDR_ANY;
416 struct hostent * p_hostent;
418 /* Try to convert address directly from in_addr - this will work if
419 * psz_address is dotted decimal. */
420 #ifdef HAVE_ARPA_INET_H
421 if( !inet_aton( psz_address, &p_socket->sin_addr ) )
423 p_socket->sin_addr.s_addr = inet_addr( psz_address );
424 if( p_socket->sin_addr.s_addr == INADDR_NONE )
427 /* We have a fqdn, try to find its address */
428 if ( (p_hostent = gethostbyname( psz_address )) == NULL )
433 /* Copy the first address of the host in the socket address */
434 memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
435 p_hostent->h_length );
443 * listen on a host for a httpd instance
446 static httpd_host_t *_RegisterHost( httpd_sys_t *p_httpt, char *psz_host_addr, int i_port )
448 httpd_host_t *p_host;
449 struct sockaddr_in sock;
453 #if !defined( WIN32 ) && !defined( UNDER_CE )
457 if( BuildAddr( &sock, psz_host_addr, i_port ) )
459 msg_Err( p_httpt, "cannot build address for %s:%d", psz_host_addr, i_port );
463 /* is it already declared ? */
464 vlc_mutex_lock( &p_httpt->host_lock );
465 for( i = 0; i < p_httpt->i_host_count; i++ )
467 if( p_httpt->host[i]->sock.sin_port == sock.sin_port &&
468 p_httpt->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr )
474 if( i < p_httpt->i_host_count )
476 /* yes, increment ref count and succed */
477 p_httpt->host[i]->i_ref++;
478 vlc_mutex_unlock( &p_httpt->host_lock );
479 return( p_httpt->host[i] );
482 /* need to add a new listening socket */
485 fd = socket( AF_INET, SOCK_STREAM, 0 );
488 msg_Err( p_httpt, "cannot open socket" );
493 if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
494 (void *) &i_opt, sizeof( i_opt ) ) < 0 )
496 msg_Warn( p_httpt, "cannot configure socket (SO_REUSEADDR)" );
499 if( bind( fd, (struct sockaddr *)&sock, sizeof( struct sockaddr_in ) ) < 0 )
501 msg_Err( p_httpt, "cannot bind socket" );
504 /* set to non-blocking */
505 #if defined( WIN32 ) || defined( UNDER_CE )
507 unsigned long i_dummy = 1;
508 if( ioctlsocket( fd, FIONBIO, &i_dummy ) != 0 )
510 msg_Err( p_httpt, "cannot set socket to non-blocking mode" );
515 if( ( i_flags = fcntl( fd, F_GETFL, 0 ) ) < 0 )
517 msg_Err( p_httpt, "cannot F_GETFL socket" );
520 if( fcntl( fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
522 msg_Err( p_httpt, "cannot F_SETFL O_NONBLOCK" );
527 if( listen( fd, LISTEN_BACKLOG ) < 0 )
529 msg_Err( p_httpt, "cannot listen socket" );
535 p_httpt->host = realloc( p_httpt->host, sizeof( httpd_host_t *) * ( p_httpt->i_host_count + 1 ) );
539 p_httpt->host = malloc( sizeof( httpd_host_t *) );
541 p_host = malloc( sizeof( httpd_host_t ) );
543 p_host->psz_host_addr = strdup( psz_host_addr );
544 p_host->i_port = i_port;
548 p_httpt->host[p_httpt->i_host_count++] = p_host;
549 vlc_mutex_unlock( &p_httpt->host_lock );
554 vlc_mutex_unlock( &p_httpt->host_lock );
561 static httpd_host_t *RegisterHost( httpd_t *p_httpd, char *psz_host_addr, int i_port )
563 return( _RegisterHost( p_httpd->p_sys, psz_host_addr, i_port ) );
567 * remove a listening host for an httpd instance
569 static void _UnregisterHost( httpd_sys_t *p_httpt, httpd_host_t *p_host )
573 vlc_mutex_lock( &p_httpt->host_lock );
574 for( i = 0; i < p_httpt->i_host_count; i++ )
576 if( p_httpt->host[i] == p_host )
581 if( i >= p_httpt->i_host_count )
583 vlc_mutex_unlock( &p_httpt->host_lock );
584 msg_Err( p_httpt, "cannot unregister host" );
590 if( p_host->i_ref > 0 )
593 vlc_mutex_unlock( &p_httpt->host_lock );
598 FREE( p_host->psz_host_addr );
599 SOCKET_CLOSE( p_host->fd );
603 if( p_httpt->i_host_count <= 1 )
605 FREE( p_httpt->host );
606 p_httpt->i_host_count = 0;
612 i_move = p_httpt->i_host_count - i - 1;
616 memmove( &p_httpt->host[i],
618 i_move * sizeof( httpd_host_t * ) );
621 p_httpt->i_host_count--;
622 p_httpt->host = realloc( p_httpt->host,
623 p_httpt->i_host_count * sizeof( httpd_host_t * ) );
626 vlc_mutex_unlock( &p_httpt->host_lock );
628 static void UnregisterHost( httpd_t *p_httpd, httpd_host_t *p_host )
630 _UnregisterHost( p_httpd->p_sys, p_host );
634 static void __RegisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
637 if( p_httpt->i_file_count )
639 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * ( p_httpt->i_file_count + 1 ) );
643 p_httpt->file = malloc( sizeof( httpd_file_t *) );
646 p_httpt->file[p_httpt->i_file_count++] = p_file;
649 static httpd_file_t *_RegisterFile( httpd_sys_t *p_httpt,
650 char *psz_file, char *psz_mime,
651 char *psz_user, char *psz_password,
652 httpd_file_callback pf_get,
653 httpd_file_callback pf_post,
654 httpd_file_callback_args_t *p_args )
656 httpd_file_t *p_file;
659 vlc_mutex_lock( &p_httpt->file_lock );
660 for( i = 0; i < p_httpt->i_file_count; i++ )
662 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
667 if( i < p_httpt->i_file_count )
669 vlc_mutex_unlock( &p_httpt->file_lock );
670 msg_Err( p_httpt, "%s already registered", psz_file );
674 p_file = malloc( sizeof( httpd_file_t ) );
676 p_file->psz_file = strdup( psz_file );
677 p_file->psz_mime = strdup( psz_mime );
678 if( psz_user && *psz_user )
680 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
681 p_file->psz_user = strdup( psz_user );
682 p_file->psz_password = strdup( psz_password );
686 p_file->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
687 p_file->psz_user = NULL;
688 p_file->psz_password = NULL;
691 p_file->b_stream = VLC_FALSE;
692 p_file->p_sys = p_args;
693 p_file->pf_get = pf_get;
694 p_file->pf_post = pf_post;
696 p_file->i_buffer_size = 0;
697 p_file->i_buffer_last_pos = 0;
698 p_file->i_buffer_pos = 0;
699 p_file->p_buffer = NULL;
701 p_file->i_header_size = 0;
702 p_file->p_header = NULL;
704 __RegisterFile( p_httpt, p_file );
706 vlc_mutex_unlock( &p_httpt->file_lock );
710 static httpd_file_t *RegisterFile( httpd_t *p_httpd,
711 char *psz_file, char *psz_mime,
712 char *psz_user, char *psz_password,
713 httpd_file_callback pf_get,
714 httpd_file_callback pf_post,
715 httpd_file_callback_args_t *p_args )
717 return( _RegisterFile( p_httpd->p_sys,
718 psz_file, psz_mime, psz_user, psz_password,
719 pf_get, pf_post, p_args ) );
722 static httpd_stream_t *_RegisterStream( httpd_sys_t *p_httpt,
723 char *psz_file, char *psz_mime,
724 char *psz_user, char *psz_password )
726 httpd_stream_t *p_stream;
729 vlc_mutex_lock( &p_httpt->file_lock );
730 for( i = 0; i < p_httpt->i_file_count; i++ )
732 if( !strcmp( psz_file, p_httpt->file[i]->psz_file ) )
737 if( i < p_httpt->i_file_count )
739 vlc_mutex_unlock( &p_httpt->file_lock );
740 msg_Err( p_httpt, "%s already registered", psz_file );
744 p_stream = malloc( sizeof( httpd_stream_t ) );
746 p_stream->psz_file = strdup( psz_file );
747 p_stream->psz_mime = strdup( psz_mime );
748 if( psz_user && *psz_user )
750 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_BASIC;
751 p_stream->psz_user = strdup( psz_user );
752 p_stream->psz_password = strdup( psz_password );
756 p_stream->i_authenticate_method = HTTPD_AUTHENTICATE_NONE;
757 p_stream->psz_user = NULL;
758 p_stream->psz_password = NULL;
761 p_stream->b_stream = VLC_TRUE;
762 p_stream->p_sys = NULL;
763 p_stream->pf_get = NULL;
764 p_stream->pf_post = NULL;
766 p_stream->i_buffer_size = 5*1024*1024;
767 p_stream->i_buffer_pos = 0;
768 p_stream->i_buffer_last_pos = 0;
769 p_stream->p_buffer = malloc( p_stream->i_buffer_size );
771 p_stream->i_header_size = 0;
772 p_stream->p_header = NULL;
774 __RegisterFile( p_httpt, p_stream );
776 vlc_mutex_unlock( &p_httpt->file_lock );
780 static httpd_stream_t *RegisterStream( httpd_t *p_httpd,
781 char *psz_file, char *psz_mime,
782 char *psz_user, char *psz_password )
784 return( _RegisterStream( p_httpd->p_sys,
785 psz_file, psz_mime, psz_user, psz_password ) );
788 static void _UnregisterFile( httpd_sys_t *p_httpt, httpd_file_t *p_file )
792 vlc_mutex_lock( &p_httpt->file_lock );
793 for( i = 0; i < p_httpt->i_file_count; i++ )
795 if( !strcmp( p_file->psz_file, p_httpt->file[i]->psz_file ) )
800 if( i >= p_httpt->i_file_count )
802 vlc_mutex_unlock( &p_httpt->file_lock );
803 msg_Err( p_httpt, "cannot unregister file" );
807 if( p_file->i_ref > 0 )
809 httpd_connection_t *p_con;
810 /* force closing all connection for this file */
811 msg_Err( p_httpt, "closing all client connection" );
813 vlc_mutex_lock( &p_httpt->connection_lock );
814 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
816 httpd_connection_t *p_next;
818 p_next = p_con->p_next;
819 if( p_con->p_file == p_file )
821 httpd_ConnnectionClose( p_httpt, p_con );
825 vlc_mutex_unlock( &p_httpt->connection_lock );
828 FREE( p_file->psz_file );
829 FREE( p_file->psz_mime );
830 if( p_file->i_authenticate_method != HTTPD_AUTHENTICATE_NONE )
832 FREE( p_file->psz_user );
833 FREE( p_file->psz_password );
835 FREE( p_file->p_buffer );
836 FREE( p_file->p_header );
841 if( p_httpt->i_file_count == 1 )
843 FREE( p_httpt->file );
844 p_httpt->i_file_count = 0;
850 i_move = p_httpt->i_file_count - i - 1;
853 memmove( &p_httpt->file[i], &p_httpt->file[i + 1], sizeof( httpd_file_t *) * i_move );
855 p_httpt->i_file_count--;
856 p_httpt->file = realloc( p_httpt->file, sizeof( httpd_file_t *) * p_httpt->i_file_count );
859 vlc_mutex_unlock( &p_httpt->file_lock );
861 static void UnregisterFile( httpd_t *p_httpd, httpd_file_t *p_file )
863 _UnregisterFile( p_httpd->p_sys, p_file );
866 static void UnregisterStream( httpd_t *p_httpd, httpd_stream_t *p_stream )
868 _UnregisterFile( p_httpd->p_sys, p_stream );
873 static int _SendStream( httpd_sys_t *p_httpt, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
878 if( i_data <= 0 || p_data == NULL )
880 return( VLC_SUCCESS );
882 //fprintf( stderr, "## i_data=%d pos=%lld\n", i_data, p_stream->i_buffer_pos );
884 vlc_mutex_lock( &p_httpt->file_lock );
886 /* save this pointer (to be used by new connection) */
887 p_stream->i_buffer_last_pos = p_stream->i_buffer_pos;
889 i_pos = p_stream->i_buffer_pos % p_stream->i_buffer_size;
895 i_copy = __MIN( i_count, p_stream->i_buffer_size - i_pos );
897 memcpy( &p_stream->p_buffer[i_pos],
901 i_pos = ( i_pos + i_copy ) % p_stream->i_buffer_size;
906 p_stream->i_buffer_pos += i_data;
907 vlc_mutex_unlock( &p_httpt->file_lock );
909 return( VLC_SUCCESS );
911 static int SendStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
913 return( _SendStream( p_httpd->p_sys, p_stream, p_data, i_data ) );
916 static int HeaderStream( httpd_t *p_httpd, httpd_stream_t *p_stream, uint8_t *p_data, int i_data )
918 httpd_sys_t *p_httpt = p_httpd->p_sys;
920 vlc_mutex_lock( &p_httpt->file_lock );
922 FREE( p_stream->p_header );
923 if( p_data == NULL || i_data <= 0 )
925 p_stream->i_header_size = 0;
929 p_stream->i_header_size = i_data;
930 p_stream->p_header = malloc( i_data );
931 memcpy( p_stream->p_header,
935 vlc_mutex_unlock( &p_httpt->file_lock );
937 return( VLC_SUCCESS );
940 /****************************************************************************/
941 /****************************************************************************/
942 /****************************************************************************/
943 /****************************************************************************/
944 /****************************************************************************/
946 static int httpd_page_401_get( httpd_file_callback_args_t *p_args,
947 uint8_t *p_request, int i_request,
948 uint8_t **pp_data, int *pi_data )
952 p = *pp_data = malloc( 1024 );
954 p += sprintf( p, "<html>\n" );
955 p += sprintf( p, "<head>\n" );
956 p += sprintf( p, "<title>Error 401</title>\n" );
957 p += sprintf( p, "</head>\n" );
958 p += sprintf( p, "<body>\n" );
959 p += sprintf( p, "<h1><center> 401 authentification needed</center></h1>\n" );
960 p += sprintf( p, "<hr />\n" );
961 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
962 p += sprintf( p, "</body>\n" );
963 p += sprintf( p, "</html>\n" );
965 *pi_data = strlen( *pp_data ) + 1;
969 static int httpd_page_404_get( httpd_file_callback_args_t *p_args,
970 uint8_t *p_request, int i_request,
971 uint8_t **pp_data, int *pi_data )
975 p = *pp_data = malloc( 1024 );
977 p += sprintf( p, "<html>\n" );
978 p += sprintf( p, "<head>\n" );
979 p += sprintf( p, "<title>Error 404</title>\n" );
980 p += sprintf( p, "</head>\n" );
981 p += sprintf( p, "<body>\n" );
982 p += sprintf( p, "<h1><center> 404 Ressource not found</center></h1>\n" );
983 p += sprintf( p, "<hr />\n" );
984 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
985 p += sprintf( p, "</body>\n" );
986 p += sprintf( p, "</html>\n" );
988 *pi_data = strlen( *pp_data ) + 1;
994 static int _httpd_page_admin_get_status( httpd_file_callback_args_t *p_args,
995 uint8_t **pp_data, int *pi_data )
997 httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
998 httpd_connection_t *p_con;
999 httpd_banned_ip_t *p_ip;
1004 /* FIXME FIXME do not use static size FIXME FIXME*/
1005 p = *pp_data = malloc( 8096 );
1007 p += sprintf( p, "<html>\n" );
1008 p += sprintf( p, "<head>\n" );
1009 p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
1010 p += sprintf( p, "</head>\n" );
1011 p += sprintf( p, "<body>\n" );
1012 p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
1013 p += sprintf( p, "<h2><center>Admin page</center></h2>\n" );
1016 p += sprintf( p, "<h3>General state</h3>\n" );
1017 p += sprintf( p, "<ul>\n" );
1018 p += sprintf( p, "<li>Connection count: %d</li>\n", p_httpt->i_connection_count );
1019 //p += sprintf( p, "<li>Total bandwith: %d</li>\n", -1 );
1020 /*p += sprintf( p, "<li></li>\n" );*/
1021 p += sprintf( p, "<li>Ban count: %d</li>\n", p_httpt->i_banned_ip_count );
1022 p += sprintf( p, "</ul>\n" );
1025 /* XXX do not lock on ban_lock */
1026 p += sprintf( p, "<h3>Ban list</h3>\n" );
1027 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1028 p += sprintf( p, "<tr>\n<th>IP</th>\n<th>Action</th></tr>\n" );
1029 for( p_ip = p_httpt->p_first_banned_ip;p_ip != NULL; p_ip = p_ip->p_next )
1031 p += sprintf( p, "<tr>\n" );
1032 p += sprintf( p, "<td>%s</td>\n", p_ip->psz_ip );
1033 p += sprintf( p, "<td><form method=\"get\" action=\"\">"
1034 "<select name=\"action\">"
1035 "<option selected>unban_ip</option>"
1037 "<input type=\"hidden\" name=\"id\" value=\"%s\"/>"
1038 "<input type=\"submit\" value=\"Do it\" />"
1039 "</form></td>\n", p_ip->psz_ip);
1040 p += sprintf( p, "</tr>\n" );
1042 p += sprintf( p, "</table>\n" );
1047 vlc_mutex_lock( &p_httpt->host_lock );
1048 p += sprintf( p, "<h3>Host list</h3>\n" );
1049 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1050 p += sprintf( p, "<tr>\n<th>Host</th><th>Port</th><th>IP</th>\n</tr>\n" );
1052 for( i = 0; i < p_httpt->i_host_count; i++ )
1054 p += sprintf( p, "<tr>\n" );
1055 p += sprintf( p, "<td>%s</td>\n", p_httpt->host[i]->psz_host_addr );
1056 p += sprintf( p, "<td>%d</td>\n", p_httpt->host[i]->i_port );
1057 p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_httpt->host[i]->sock.sin_addr ) );
1058 p += sprintf( p, "</tr>\n" );
1060 p += sprintf( p, "</table>\n" );
1061 vlc_mutex_unlock( &p_httpt->host_lock );
1064 /* XXX do not take lock on file_lock */
1065 p += sprintf( p, "<h3>File list</h3>\n" );
1066 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1067 p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
1069 for( i = 0; i < p_httpt->i_file_count; i++ )
1071 if( !p_httpt->file[i]->b_stream )
1073 p += sprintf( p, "<tr>\n" );
1074 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
1075 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
1076 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
1077 p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
1078 p += sprintf( p, "</tr>\n" );
1081 p += sprintf( p, "</table>\n" );
1084 /* XXX do not take lock on file_lock */
1085 p += sprintf( p, "<h3>Stream list</h3>\n" );
1086 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1087 p += sprintf( p, "<tr>\n<th>Name</th><th>Mime</th><th>Protected</th><th>Used</th>\n</tr>\n" );
1089 for( i = 0; i < p_httpt->i_file_count; i++ )
1091 if( p_httpt->file[i]->b_stream )
1093 p += sprintf( p, "<tr>\n" );
1094 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_file );
1095 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_mime );
1096 p += sprintf( p, "<td>%s</td>\n", p_httpt->file[i]->psz_user ? "Yes" : "No" );
1097 p += sprintf( p, "<td>%d</td>\n", p_httpt->file[i]->i_ref);
1098 p += sprintf( p, "</tr>\n" );
1101 p += sprintf( p, "</table>\n" );
1103 /* connection list */
1104 /* XXX do not take lock on connection_lock */
1105 p += sprintf( p, "<h3>Connection list</h3>\n" );
1106 p += sprintf( p, "<table border=\"1\" cellspacing=\"0\" >\n" );
1107 p += sprintf( p, "<tr>\n<th>IP</th><th>Requested File</th><th>Status</th><th>Action</th>\n</tr>\n" );
1109 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1111 p += sprintf( p, "<tr>\n" );
1112 p += sprintf( p, "<td>%s</td>\n", inet_ntoa( p_con->sock.sin_addr ) );
1113 p += sprintf( p, "<td>%s</td>\n", p_con->psz_file );
1114 p += sprintf( p, "<td>%d</td>\n", p_con->i_http_error );
1115 p += sprintf( p, "<td><form method=\"get\" action=\"\">"
1116 "<select name=\"action\">"
1117 "<option selected>close_connection</option>"
1118 "<option>ban_ip</option>"
1119 "<option>close_connection_and_ban_ip</option>"
1121 "<input type=\"hidden\" name=\"id\" value=\"%p\"/>"
1122 "<input type=\"submit\" value=\"Do it\" />"
1123 "</form></td>\n", p_con);
1124 p += sprintf( p, "</tr>\n" );
1126 p += sprintf( p, "</table>\n" );
1129 /* www.videolan.org */
1130 p += sprintf( p, "<hr />\n" );
1131 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1132 p += sprintf( p, "</body>\n" );
1133 p += sprintf( p, "</html>\n" );
1135 *pi_data = strlen( *pp_data ) + 1;
1137 return( VLC_SUCCESS );
1140 static int _httpd_page_admin_get_success( httpd_file_callback_args_t *p_args,
1141 uint8_t **pp_data, int *pi_data,
1146 p = *pp_data = malloc( 8096 );
1148 p += sprintf( p, "<html>\n" );
1149 p += sprintf( p, "<head>\n" );
1150 p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
1151 p += sprintf( p, "</head>\n" );
1152 p += sprintf( p, "<body>\n" );
1153 p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
1155 p += sprintf( p, "<p>Success=`%s'</p>", psz_msg );
1156 p += sprintf( p, "<a href=\"admin.html\">Retour a la page d'administration</a>\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;
1165 return( VLC_SUCCESS );
1168 static int _httpd_page_admin_get_error( httpd_file_callback_args_t *p_args,
1169 uint8_t **pp_data, int *pi_data,
1174 p = *pp_data = malloc( 8096 );
1176 p += sprintf( p, "<html>\n" );
1177 p += sprintf( p, "<head>\n" );
1178 p += sprintf( p, "<title>VideoLAN Client Stream Output</title>\n" );
1179 p += sprintf( p, "</head>\n" );
1180 p += sprintf( p, "<body>\n" );
1181 p += sprintf( p, "<h1><center>VideoLAN Client Stream Output</center></h1>\n" );
1183 p += sprintf( p, "<p>Error=`%s'</p>", psz_error );
1184 p += sprintf( p, "<a href=\"admin.html\">Retour a la page d'administration</a>\n" );
1186 p += sprintf( p, "<hr />\n" );
1187 p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1188 p += sprintf( p, "</body>\n" );
1189 p += sprintf( p, "</html>\n" );
1191 *pi_data = strlen( *pp_data ) + 1;
1193 return( VLC_SUCCESS );
1196 static void _httpd_uri_extract_value( char *psz_uri, char *psz_name, char *psz_value, int i_value_max )
1200 p = strstr( psz_uri, psz_name );
1205 p += strlen( psz_name );
1206 if( *p == '=' ) p++;
1208 if( strchr( p, '&' ) )
1210 i_len = strchr( p, '&' ) - p;
1214 i_len = strlen( p );
1216 i_len = __MIN( i_value_max - 1, i_len );
1219 strncpy( psz_value, p, i_len );
1220 psz_value[i_len] = '\0';
1224 strncpy( psz_value, "", i_value_max );
1229 strncpy( psz_value, "", i_value_max );
1234 static int httpd_page_admin_get( httpd_file_callback_args_t *p_args,
1235 uint8_t *p_request, int i_request,
1236 uint8_t **pp_data, int *pi_data )
1238 httpd_sys_t *p_httpt = (httpd_sys_t*)p_args;
1239 httpd_connection_t *p_con;
1245 _httpd_uri_extract_value( p_request, "action", action, 512 );
1247 if( !strcmp( action, "close_connection" ) )
1252 _httpd_uri_extract_value( p_request, "id", id, 512 );
1253 i_id = (void*)strtol( id, NULL, 0 );
1254 msg_Dbg( p_httpt, "requested closing connection id=%s %p", id, i_id );
1255 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1257 if( (void*)p_con == i_id )
1259 /* XXX don't free p_con as it could be the one that it is sending ... */
1260 p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1261 return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "connection closed" ) );
1264 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, "invalid id" ) );
1266 else if( !strcmp( action, "ban_ip" ) )
1271 _httpd_uri_extract_value( p_request, "id", id, 512 );
1272 i_id = (void*)strtol( id, NULL, 0 );
1274 msg_Dbg( p_httpt, "requested banning ip id=%s %p", id, i_id );
1276 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1278 if( (void*)p_con == i_id )
1280 if( httpd_BanIP( p_httpt,inet_ntoa( p_con->sock.sin_addr ) ) == 0)
1281 return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "IP banned" ) );
1287 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1289 else if( !strcmp( action, "unban_ip" ) )
1293 _httpd_uri_extract_value( p_request, "id", id, 512 );
1294 msg_Dbg( p_httpt, "requested unbanning ip %s", id);
1296 if( httpd_UnbanIP( p_httpt, httpd_GetbannedIP ( p_httpt, id ) ) == 0)
1297 return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "IP Unbanned" ) );
1299 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1301 else if( !strcmp( action, "close_connection_and_ban_ip" ) )
1306 _httpd_uri_extract_value( p_request, "id", id, 512 );
1307 i_id = (void*)strtol( id, NULL, 0 );
1308 msg_Dbg( p_httpt, "requested closing connection and banning ip id=%s %p", id, i_id );
1309 for( p_con = p_httpt->p_first_connection;p_con != NULL; p_con = p_con->p_next )
1311 if( (void*)p_con == i_id )
1313 /* XXX don't free p_con as it could be the one that it is sending ... */
1314 p_con->i_state = HTTPD_CONNECTION_TO_BE_CLOSED;
1316 if( httpd_BanIP( p_httpt,inet_ntoa( p_con->sock.sin_addr ) ) == 0)
1317 return( _httpd_page_admin_get_success( p_args, pp_data, pi_data, "Connection closed and IP banned" ) );
1323 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, "invalid id" ) );
1326 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1330 return( _httpd_page_admin_get_error( p_args, pp_data, pi_data, action ) );
1335 return( _httpd_page_admin_get_status( p_args, pp_data, pi_data ) );
1341 static int httpd_BanIP( httpd_sys_t *p_httpt, char * psz_new_banned_ip)
1343 httpd_banned_ip_t *p_new_banned_ip ;
1345 p_new_banned_ip = malloc( sizeof( httpd_banned_ip_t ) );
1346 if( !p_new_banned_ip )
1350 p_new_banned_ip->p_next=NULL;
1351 p_new_banned_ip->psz_ip = malloc( strlen( psz_new_banned_ip ) + 1 );
1352 if( !p_new_banned_ip->psz_ip )
1357 strcpy( p_new_banned_ip->psz_ip, psz_new_banned_ip );
1359 msg_Dbg( p_httpt, "Banning IP %s", psz_new_banned_ip );
1361 if( p_httpt->p_first_banned_ip )
1363 httpd_banned_ip_t *p_last;
1365 p_last = p_httpt->p_first_banned_ip;
1366 while( p_last->p_next )
1368 p_last = p_last->p_next;
1371 p_last->p_next = p_new_banned_ip;
1372 p_new_banned_ip->p_prev = p_last;
1376 p_new_banned_ip->p_prev = NULL;
1378 p_httpt->p_first_banned_ip = p_new_banned_ip;
1381 p_httpt->i_banned_ip_count++;
1385 static httpd_banned_ip_t *httpd_GetbannedIP( httpd_sys_t *p_httpt, char *psz_ip )
1387 httpd_banned_ip_t *p_ip;
1389 p_ip = p_httpt->p_first_banned_ip;
1393 if( strcmp( psz_ip, p_ip->psz_ip ) == 0 )
1397 p_ip = p_ip->p_next;
1403 static int httpd_UnbanIP( httpd_sys_t *p_httpt, httpd_banned_ip_t *p_banned_ip )
1410 msg_Dbg( p_httpt, "Unbanning IP %s",p_banned_ip->psz_ip);
1412 /* first cut out from list */
1413 if( p_banned_ip->p_prev )
1415 p_banned_ip->p_prev->p_next = p_banned_ip->p_next;
1419 p_httpt->p_first_banned_ip = p_banned_ip->p_next;
1422 if( p_banned_ip->p_next )
1424 p_banned_ip->p_next->p_prev = p_banned_ip->p_prev;
1427 FREE( p_banned_ip->psz_ip );
1428 FREE( p_banned_ip );
1430 p_httpt->i_banned_ip_count--;
1436 static void httpd_ConnnectionNew( httpd_sys_t *p_httpt, int fd, struct sockaddr_in *p_sock )
1438 httpd_connection_t *p_con;
1440 msg_Dbg( p_httpt, "new connection from %s", inet_ntoa( p_sock->sin_addr ) );
1442 /* verify if it's a banned ip */
1443 if(httpd_GetbannedIP( p_httpt,inet_ntoa( p_sock->sin_addr ) ) )
1445 msg_Dbg( p_httpt, "Ip %s banned : closing connection", inet_ntoa( p_sock->sin_addr ) );
1450 /* create a new connection and link it */
1451 p_con = malloc( sizeof( httpd_connection_t ) );
1452 p_con->i_state = HTTPD_CONNECTION_RECEIVING_REQUEST;
1454 p_con->i_last_activity_date = mdate();
1456 p_con->sock = *p_sock;
1457 p_con->psz_file = NULL;
1458 p_con->i_http_error = 0;
1459 p_con->psz_user = NULL;
1460 p_con->psz_password = NULL;
1461 p_con->p_file = NULL;
1463 p_con->i_request_size = 0;
1464 p_con->p_request = NULL;
1466 p_con->i_buffer = 0;
1467 p_con->i_buffer_size = 8096;
1468 p_con->p_buffer = malloc( p_con->i_buffer_size );
1470 p_con->i_stream_pos = 0; // updated by httpd_thread */
1471 p_con->p_next = NULL;
1473 if( p_httpt->p_first_connection )
1475 httpd_connection_t *p_last;
1477 p_last = p_httpt->p_first_connection;
1478 while( p_last->p_next )
1480 p_last = p_last->p_next;
1483 p_last->p_next = p_con;
1484 p_con->p_prev = p_last;
1488 p_con->p_prev = NULL;
1490 p_httpt->p_first_connection = p_con;
1493 p_httpt->i_connection_count++;
1496 static void httpd_ConnnectionClose( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1498 msg_Dbg( p_httpt, "close connection from %s", inet_ntoa( p_con->sock.sin_addr ) );
1500 p_httpt->i_connection_count--;
1501 /* first cut out from list */
1504 p_con->p_prev->p_next = p_con->p_next;
1508 p_httpt->p_first_connection = p_con->p_next;
1513 p_con->p_next->p_prev = p_con->p_prev;
1516 if( p_con->p_file ) p_con->p_file->i_ref--;
1517 FREE( p_con->psz_file );
1519 FREE( p_con->p_buffer );
1520 SOCKET_CLOSE( p_con->fd );
1522 FREE( p_con->psz_user );
1523 FREE( p_con->psz_password );
1525 FREE( p_con->p_request );
1529 static void httpd_RequestGetWord( char *word, int i_word_max, char **pp_buffer, char *p_end )
1531 char *p = *pp_buffer;
1534 while( p < p_end && *p && ( *p == ' ' || *p == '\t' ) )
1540 for( i = 0; i < i_word_max && p < p_end && *p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'; i++,p++)
1545 word[__MIN( i, i_word_max -1 )] = '\0';
1550 static int httpd_RequestNextLine( char **pp_buffer, char *p_end )
1554 for( p = *pp_buffer; p < p_end; p++ )
1556 if( p + 1 < p_end && *p == '\n' )
1561 if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
1568 return VLC_EGENERIC;
1571 //char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1572 static void b64_decode( char *dest, char *src )
1577 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
1578 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
1579 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
1580 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
1581 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
1582 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
1583 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
1584 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
1585 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
1586 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
1587 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
1588 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
1589 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
1590 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
1591 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
1592 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
1595 for( i_level = 0; *src != '\0'; src++ )
1599 c = b64[(unsigned int)*src];
1612 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
1616 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
1620 *dest++ = ( ( last &0x03 ) << 6 ) | c;
1629 static void httpd_ConnectionParseRequest( httpd_sys_t *p_httpt, httpd_connection_t *p_con )
1638 char user[512] = "";
1639 char password[512] = "";
1641 //msg_Dbg( p_httpt, "new request=\n%s", p_con->p_buffer );
1644 p = p_con->p_buffer;
1645 p_end = p + strlen( p ) + 1;
1647 httpd_RequestGetWord( command, 32, &p, p_end );
1648 httpd_RequestGetWord( url, 1024, &p, p_end );
1649 httpd_RequestGetWord( version, 32, &p, p_end );
1650 //msg_Dbg( p_httpt, "ask =%s= =%s= =%s=", command, url, version );
1652 p_con->p_request = NULL;
1653 p_con->i_request_size = 0;
1654 if( !strcmp( command, "GET" ) )
1656 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1658 else if( !strcmp( command, "POST" ))
1660 p_con->i_method = HTTPD_CONNECTION_METHOD_POST;
1665 p_con->psz_file = strdup( "/501.html" );
1666 p_con->i_method = HTTPD_CONNECTION_METHOD_GET;
1667 p_con->i_http_error = 501;
1671 if( strcmp( version, "HTTP/1.0" ) && strcmp( version, "HTTP/1.1" ) )
1673 p_con->psz_file = strdup( "/505.html" );
1674 p_con->i_http_error = 505;
1684 if( httpd_RequestNextLine( &p, p_end ) )
1686 //msg_Dbg( p_httpt, "failled new line" );
1689 //msg_Dbg( p_httpt, "new line=%s", p );
1691 httpd_RequestGetWord( header, 1024, &p, p_end );
1692 if( !strcmp( header, "\r\n" ) || !strcmp( header, "\n" ) )
1697 if( !strcmp( header, "Authorization:" ) )
1701 httpd_RequestGetWord( method, 32, &p, p_end );
1702 if( !strcasecmp( method, "BASIC" ) )
1707 httpd_RequestGetWord( basic, 1024, &p, p_end );
1708 //msg_Dbg( p_httpt, "Authorization: basic:%s", basic );
1709 b64_decode( decoded, basic );
1711 //msg_Dbg( p_httpt, "Authorization: decoded:%s", decoded );
1712 if( strchr( decoded, ':' ) )
1714 char *p = strchr( decoded, ':' );
1717 strcpy( user, decoded );
1718 strcpy( password, p );
1724 if( strchr( url, '?' ) )
1726 char *p_request = strchr( url, '?' );
1727 *p_request++ = '\0';
1728 p_con->psz_file = strdup( url );
1729 p_con->p_request = strdup( p_request );
1730 p_con->i_request_size = strlen( p_con->p_request );
1734 p_con->psz_file = strdup( url );
1738 if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
1741 if( strstr( p_con->p_buffer, "\r\n\r\n" ) )
1743 p_request = strstr( p_con->p_buffer, "\r\n\r\n" ) + 4;
1745 else if( strstr( p_con->p_buffer, "\n\n" ) )
1747 p_request = strstr( p_con->p_buffer, "\n\n" ) + 2;
1753 if( p_request && p_request < p_end )
1755 p_con->i_request_size = p_end - p_request;
1756 p_con->p_request = malloc( p_con->i_request_size + 1);
1758 memcpy( p_con->p_request,
1760 p_con->i_request_size );
1762 p_con->p_request[p_con->i_request_size] = '\0';
1765 p_con->i_http_error = 200;
1768 //msg_Dbg( p_httpt, "ask %s %s %d", command, p_con->psz_file, p_con->i_http_error );
1769 FREE( p_con->p_buffer );
1770 p_con->i_buffer = 0;
1771 p_con->i_buffer_size = 0;
1773 //vlc_mutex_lock( &p_httpt->file_lock );
1776 p_con->p_file = NULL;
1777 for( i = 0; i < p_httpt->i_file_count; i++ )
1779 if( !strcmp( p_httpt->file[i]->psz_file, p_con->psz_file ) )
1781 if( p_httpt->file[i]->b_stream ||
1782 ( p_con->i_method == HTTPD_CONNECTION_METHOD_GET && p_httpt->file[i]->pf_get ) ||
1783 ( p_con->i_method == HTTPD_CONNECTION_METHOD_POST && p_httpt->file[i]->pf_post ) )
1785 p_con->p_file = p_httpt->file[i];
1791 if( !p_con->p_file )
1793 p_con->psz_file = strdup( "/404.html" );
1794 p_con->i_http_error = 404;
1796 /* XXX be sure that "/404.html" exist else ... */
1800 if( p_con->p_file->i_authenticate_method == HTTPD_AUTHENTICATE_BASIC )
1802 if( strcmp( user, p_con->p_file->psz_user ) || strcmp( password, p_con->p_file->psz_password ) )
1804 p_con->psz_file = strdup( "/401.html" );
1805 strcpy( user, p_con->p_file->psz_user );
1806 p_con->i_http_error = 401;
1808 /* XXX do not put password on 404 else ... */
1813 p_con->p_file->i_ref++;
1814 // vlc_mutex_unlock( &p_httpt->file_lock );
1816 switch( p_con->i_http_error )
1823 psz_status = "Authorization Required";
1826 psz_status = "Unknown";
1830 p_con->i_state = HTTPD_CONNECTION_SENDING_HEADER;
1832 /* we send stream header with this one */
1833 if( p_con->i_http_error == 200 && p_con->p_file->b_stream )
1835 p_con->i_buffer_size = 4096 + p_con->p_file->i_header_size;
1838 p_con->i_buffer_size = 4096;
1839 p_con->i_buffer = 0;
1840 p = p_con->p_buffer = malloc( p_con->i_buffer_size );
1842 p += sprintf( p, "HTTP/1.0 %d %s\r\n", p_con->i_http_error, psz_status );
1843 p += sprintf( p, "Content-type: %s\r\n", p_con->p_file->psz_mime );
1844 if( p_con->i_http_error == 401 )
1846 p += sprintf( p, "WWW-Authenticate: Basic realm=\"%s\"\r\n", user );
1848 p += sprintf( p, "Cache-Control: no-cache\r\n" );
1849 p += sprintf( p, "\r\n" );
1851 p_con->i_buffer_size = strlen( p_con->p_buffer );// + 1;
1853 if( p_con->i_http_error == 200 && p_con->p_file->b_stream && p_con->p_file->i_header_size > 0 )
1855 /* add stream header */
1856 memcpy( &p_con->p_buffer[p_con->i_buffer_size],
1857 p_con->p_file->p_header,
1858 p_con->p_file->i_header_size );
1859 p_con->i_buffer_size += p_con->p_file->i_header_size;
1862 //msg_Dbg( p_httpt, "answer=\n%s", p_con->p_buffer );
1864 #define HTTPD_STREAM_PACKET 10000
1865 static void httpd_Thread( httpd_sys_t *p_httpt )
1867 httpd_file_t *p_page_admin;
1868 httpd_file_t *p_page_401;
1869 httpd_file_t *p_page_404;
1871 httpd_connection_t *p_con;
1873 msg_Info( p_httpt, "httpd started" );
1875 p_page_401 = _RegisterFile( p_httpt,
1876 "/401.html", "text/html",
1880 (httpd_file_callback_args_t*)NULL );
1881 p_page_404 = _RegisterFile( p_httpt,
1882 "/404.html", "text/html",
1886 (httpd_file_callback_args_t*)NULL );
1887 p_page_admin = _RegisterFile( p_httpt,
1888 "/admin.html", "text/html",
1890 httpd_page_admin_get,
1892 (httpd_file_callback_args_t*)p_httpt );
1894 while( !p_httpt->b_die )
1896 struct timeval timeout;
1899 int i_handle_max = 0;
1902 if( p_httpt->i_host_count <= 0 )
1904 msleep( 100 * 1000 );
1908 /* we will create a socket set with host and connection */
1909 FD_ZERO( &fds_read );
1910 FD_ZERO( &fds_write );
1912 vlc_mutex_lock( &p_httpt->host_lock );
1913 vlc_mutex_lock( &p_httpt->connection_lock );
1914 for( i = 0; i < p_httpt->i_host_count; i++ )
1916 FD_SET( p_httpt->host[i]->fd, &fds_read );
1917 i_handle_max = __MAX( i_handle_max, p_httpt->host[i]->fd );
1919 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
1921 /* no more than 10s of inactivity */
1922 if( p_con->i_last_activity_date + (mtime_t)HTTPD_CONNECTION_MAX_UNUSED < mdate() ||
1923 p_con->i_state == HTTPD_CONNECTION_TO_BE_CLOSED)
1925 httpd_connection_t *p_next = p_con->p_next;
1927 msg_Dbg( p_httpt, "close unused connection" );
1928 httpd_ConnnectionClose( p_httpt, p_con );
1933 if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM && p_con->i_stream_pos + HTTPD_STREAM_PACKET >= p_con->p_file->i_buffer_pos )
1935 p_con = p_con->p_next;
1939 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
1941 FD_SET( p_con->fd, &fds_read );
1945 FD_SET( p_con->fd, &fds_write );
1947 i_handle_max = __MAX( i_handle_max, p_con->fd );
1949 p_con = p_con->p_next;
1951 vlc_mutex_unlock( &p_httpt->host_lock );
1952 vlc_mutex_unlock( &p_httpt->connection_lock );
1954 /* we will wait 0.5s */
1956 timeout.tv_usec = 500*1000;
1958 i_ret = select( i_handle_max + 1,
1963 if( i_ret == -1 && errno != EINTR )
1965 msg_Warn( p_httpt, "cannot select sockets" );
1969 /*FIXME : < 0 is necessary for medialive plugin (it was <=0 before), but why ?*/
1972 // msg_Dbg( p_httpt, "waiting..." );
1976 vlc_mutex_lock( &p_httpt->host_lock );
1977 /* accept/refuse new connection */
1978 for( i = 0; i < p_httpt->i_host_count; i++ )
1980 int i_sock_size = sizeof( struct sockaddr_in );
1981 struct sockaddr_in sock;
1984 fd = accept( p_httpt->host[i]->fd, (struct sockaddr *)&sock,
1988 #if defined( WIN32 ) || defined( UNDER_CE )
1990 unsigned long i_dummy = 1;
1991 ioctlsocket( fd, FIONBIO, &i_dummy );
1994 fcntl( fd, F_SETFL, O_NONBLOCK );
1997 if( p_httpt->i_connection_count >= HTTPD_MAX_CONNECTION )
1999 msg_Warn( p_httpt, "max connection reached" );
2003 /* create a new connection and link it */
2004 httpd_ConnnectionNew( p_httpt, fd, &sock );
2008 vlc_mutex_unlock( &p_httpt->host_lock );
2010 vlc_mutex_lock( &p_httpt->file_lock );
2011 /* now do work for all connections */
2012 for( p_con = p_httpt->p_first_connection; p_con != NULL; )
2014 if( p_con->i_state == HTTPD_CONNECTION_RECEIVING_REQUEST )
2018 i_len = recv( p_con->fd,
2019 p_con->p_buffer + p_con->i_buffer,
2020 p_con->i_buffer_size - p_con->i_buffer, 0 );
2023 #if defined( WIN32 ) || defined( UNDER_CE )
2024 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) ||
2026 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) ||
2030 httpd_connection_t *p_next = p_con->p_next;
2032 httpd_ConnnectionClose( p_httpt, p_con );
2035 else if( i_len > 0 )
2038 p_con->i_last_activity_date = mdate();
2039 p_con->i_buffer += i_len;
2041 ptr = p_con->p_buffer + p_con->i_buffer;
2043 if( ( p_con->i_buffer >= 2 && !strncmp( ptr - 2, "\n\n", 2 ) )||
2044 ( p_con->i_buffer >= 4 && !strncmp( ptr - 4, "\r\n\r\n", 4 ) ) ||
2045 p_con->i_buffer >= p_con->i_buffer_size )
2047 p_con->p_buffer[__MIN( p_con->i_buffer, p_con->i_buffer_size - 1 )] = '\0';
2048 httpd_ConnectionParseRequest( p_httpt, p_con );
2051 p_con = p_con->p_next;
2055 p_con = p_con->p_next;
2057 continue; /* just for clarity */
2059 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER || p_con->i_state == HTTPD_CONNECTION_SENDING_FILE )
2064 if( p_con->i_buffer_size - p_con->i_buffer > 0 )
2066 i_len = send( p_con->fd, p_con->p_buffer + p_con->i_buffer, p_con->i_buffer_size - p_con->i_buffer, 0 );
2072 // 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 );
2074 #if defined( WIN32 ) || defined( UNDER_CE )
2075 if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) ||
2077 if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) ||
2081 httpd_connection_t *p_next = p_con->p_next;
2083 httpd_ConnnectionClose( p_httpt, p_con );
2086 else if( i_len > 0 )
2088 p_con->i_last_activity_date = mdate();
2089 p_con->i_buffer += i_len;
2091 if( p_con->i_buffer >= p_con->i_buffer_size )
2093 if( p_con->i_state == HTTPD_CONNECTION_SENDING_HEADER )
2095 p_con->i_buffer_size = 0;
2096 p_con->i_buffer = 0;
2097 FREE( p_con->p_buffer );
2099 if( !p_con->p_file->b_stream )
2101 p_con->i_state = HTTPD_CONNECTION_SENDING_FILE; // be sure to out from HTTPD_CONNECTION_SENDING_HEADER
2102 if( p_con->i_method == HTTPD_CONNECTION_METHOD_GET )
2104 p_con->p_file->pf_get( p_con->p_file->p_sys,
2105 p_con->p_request, p_con->i_request_size,
2106 &p_con->p_buffer, &p_con->i_buffer_size );
2108 else if( p_con->i_method == HTTPD_CONNECTION_METHOD_POST )
2110 p_con->p_file->pf_post( p_con->p_file->p_sys,
2111 p_con->p_request, p_con->i_request_size,
2112 &p_con->p_buffer, &p_con->i_buffer_size );
2116 p_con->p_buffer = NULL;
2117 p_con->i_buffer_size = 0;
2122 p_con->i_state = HTTPD_CONNECTION_SENDING_STREAM;
2123 p_con->i_stream_pos = p_con->p_file->i_buffer_last_pos;
2125 p_con = p_con->p_next;
2129 httpd_connection_t *p_next = p_con->p_next;
2131 httpd_ConnnectionClose( p_httpt, p_con );
2137 p_con = p_con->p_next;
2142 p_con = p_con->p_next;
2144 continue; /* just for clarity */
2146 else if( p_con->i_state == HTTPD_CONNECTION_SENDING_STREAM )
2148 httpd_stream_t *p_stream = p_con->p_file;
2152 if( p_con->i_stream_pos < p_stream->i_buffer_pos )
2155 /* check if this p_con aren't to late */
2156 if( p_con->i_stream_pos + p_stream->i_buffer_size < p_stream->i_buffer_pos )
2158 fprintf(stderr, "fixing i_stream_pos (old=%lld i_buffer_pos=%lld\n", p_con->i_stream_pos, p_stream->i_buffer_pos );
2159 p_con->i_stream_pos = p_stream->i_buffer_last_pos;
2162 i_pos = p_con->i_stream_pos % p_stream->i_buffer_size;
2163 /* size until end of buffer */
2164 i_write = p_stream->i_buffer_size - i_pos;
2165 /* is it more than valid data */
2166 if( i_write >= p_stream->i_buffer_pos - p_con->i_stream_pos )
2168 i_write = p_stream->i_buffer_pos - p_con->i_stream_pos;
2170 /* limit to HTTPD_STREAM_PACKET */
2171 if( i_write > HTTPD_STREAM_PACKET )
2173 i_write = HTTPD_STREAM_PACKET;
2175 i_send = send( p_con->fd, &p_stream->p_buffer[i_pos], i_write, 0 );
2177 #if defined( WIN32 ) || defined( UNDER_CE )
2178 if( ( i_send < 0 && WSAGetLastError() != WSAEWOULDBLOCK )||
2180 if( ( i_send < 0 && errno != EAGAIN && errno != EINTR )||
2184 httpd_connection_t *p_next = p_con->p_next;
2186 httpd_ConnnectionClose( p_httpt, p_con );
2190 else if( i_send > 0 )
2192 msg_Dbg( p_httpt, "Sending %d bytes",i_send );
2193 p_con->i_last_activity_date = mdate();
2194 p_con->i_stream_pos += i_send;
2197 p_con = p_con->p_next;
2198 continue; /* just for clarity */
2200 else if( p_con->i_state != HTTPD_CONNECTION_TO_BE_CLOSED )
2202 msg_Warn( p_httpt, "cannot occur (Invalid p_con->i_state)" );
2203 p_con = p_con->p_next;
2205 } /* for over connection */
2207 vlc_mutex_unlock( &p_httpt->file_lock );
2209 msg_Info( p_httpt, "httpd stopped" );
2211 _UnregisterFile( p_httpt, p_page_401 );
2212 _UnregisterFile( p_httpt, p_page_404 );
2213 _UnregisterFile( p_httpt, p_page_admin );