]> git.sesse.net Git - vlc/blob - src/network/httpd.c
b9ab16fa6f843e82191d1a65e649f27a84ea0630
[vlc] / src / network / httpd.c
1 /*****************************************************************************
2  * httpd.c
3  *****************************************************************************
4  * Copyright (C) 2004-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          RĂ©mi Denis-Courmont <rem # videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include <stdlib.h>
26 #include <vlc/vlc.h>
27
28 #ifdef ENABLE_HTTPD
29
30 #include "vlc_httpd.h"
31 #include "network.h"
32 #include "vlc_tls.h"
33 #include "vlc_acl.h"
34
35 #include <string.h>
36 #include <errno.h>
37
38 #ifdef HAVE_UNISTD_H
39 #   include <unistd.h>
40 #endif
41
42 #ifdef HAVE_FCNTL_H
43 #   include <fcntl.h>
44 #endif
45
46 #if defined( UNDER_CE )
47 #   include <winsock.h>
48 #elif defined( WIN32 )
49 #   include <winsock2.h>
50 #   include <ws2tcpip.h>
51 #else
52 #   include <netdb.h>                                         /* hostent ... */
53 #   include <sys/socket.h>
54 /* FIXME: should not be needed */
55 #   include <netinet/in.h>
56 #   ifdef HAVE_ARPA_INET_H
57 #       include <arpa/inet.h>                    /* inet_ntoa(), inet_aton() */
58 #   endif
59 #endif
60
61 #if defined( WIN32 )
62 /* We need HUGE buffer otherwise TCP throughput is very limited */
63 #define HTTPD_CL_BUFSIZE 1000000
64 #else
65 #define HTTPD_CL_BUFSIZE 10000
66 #endif
67
68 static void httpd_ClientClean( httpd_client_t *cl );
69
70 /* each host run in his own thread */
71 struct httpd_host_t
72 {
73     VLC_COMMON_MEMBERS
74
75     httpd_t     *httpd;
76
77     /* ref count */
78     int         i_ref;
79
80     /* address/port and socket for listening at connections */
81     char        *psz_hostname;
82     int         i_port;
83     int         *fd;
84
85     vlc_mutex_t lock;
86
87     /* all registered url (becarefull that 2 httpd_url_t could point at the same url)
88      * This will slow down the url research but make my live easier
89      * All url will have their cb trigger, but only the first one can answer
90      * */
91     int         i_url;
92     httpd_url_t **url;
93
94     int            i_client;
95     httpd_client_t **client;
96
97     /* TLS data */
98     tls_server_t *p_tls;
99 };
100
101 struct httpd_url_t
102 {
103     httpd_host_t *host;
104
105     vlc_mutex_t lock;
106
107     char      *psz_url;
108     char      *psz_user;
109     char      *psz_password;
110     vlc_acl_t *p_acl;
111
112     struct
113     {
114         httpd_callback_t     cb;
115         httpd_callback_sys_t *p_sys;
116     } catch[HTTPD_MSG_MAX];
117 };
118
119 /* status */
120 enum
121 {
122     HTTPD_CLIENT_RECEIVING,
123     HTTPD_CLIENT_RECEIVE_DONE,
124
125     HTTPD_CLIENT_SENDING,
126     HTTPD_CLIENT_SEND_DONE,
127
128     HTTPD_CLIENT_WAITING,
129
130     HTTPD_CLIENT_DEAD,
131
132     HTTPD_CLIENT_TLS_HS_IN,
133     HTTPD_CLIENT_TLS_HS_OUT
134 };
135
136 /* mode */
137 enum
138 {
139     HTTPD_CLIENT_FILE,      /* default */
140     HTTPD_CLIENT_STREAM,    /* regulary get data from cb */
141     HTTPD_CLIENT_BIDIR,     /* check for reading and get data from cb */
142 };
143
144 struct httpd_client_t
145 {
146     httpd_url_t *url;
147
148     int     i_ref;
149
150     struct  sockaddr_storage sock;
151     int     i_sock_size;
152     int     fd;
153
154     int     i_mode;
155     int     i_state;
156     int     b_read_waiting; /* stop as soon as possible sending */
157
158     mtime_t i_activity_date;
159     mtime_t i_activity_timeout;
160
161     /* buffer for reading header */
162     int     i_buffer_size;
163     int     i_buffer;
164     uint8_t *p_buffer;
165
166     /* */
167     httpd_message_t query;  /* client -> httpd */
168     httpd_message_t answer; /* httpd -> client */
169
170     /* TLS data */
171     tls_session_t *p_tls;
172 };
173
174
175 /*****************************************************************************
176  * Various functions
177  *****************************************************************************/
178 /*char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/
179 static void b64_decode( char *dest, char *src )
180 {
181     int  i_level;
182     int  last = 0;
183     int  b64[256] = {
184         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
185         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
186         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
187         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
188         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
189         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
190         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
191         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
192         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
193         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
194         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
195         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
196         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
197         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
198         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
199         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
200         };
201
202     for( i_level = 0; *src != '\0'; src++ )
203     {
204         int  c;
205
206         c = b64[(unsigned int)*src];
207         if( c == -1 )
208         {
209             continue;
210         }
211
212         switch( i_level )
213         {
214             case 0:
215                 i_level++;
216                 break;
217             case 1:
218                 *dest++ = ( last << 2 ) | ( ( c >> 4)&0x03 );
219                 i_level++;
220                 break;
221             case 2:
222                 *dest++ = ( ( last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
223                 i_level++;
224                 break;
225             case 3:
226                 *dest++ = ( ( last &0x03 ) << 6 ) | c;
227                 i_level = 0;
228         }
229         last = c;
230     }
231
232     *dest = '\0';
233 }
234
235 static struct
236 {
237     const char *psz_ext;
238     const char *psz_mime;
239 } http_mime[] =
240 {
241     { ".htm",   "text/html" },
242     { ".html",  "text/html" },
243     { ".txt",   "text/plain" },
244     { ".xml",   "text/xml" },
245     { ".dtd",   "text/dtd" },
246
247     { ".css",   "text/css" },
248
249     /* image mime */
250     { ".gif",   "image/gif" },
251     { ".jpe",   "image/jpeg" },
252     { ".jpg",   "image/jpeg" },
253     { ".jpeg",  "image/jpeg" },
254     { ".png",   "image/png" },
255     { ".mpjpeg","multipart/x-mixed-replace; boundary=This Random String" },
256
257     /* media mime */
258     { ".avi",   "video/avi" },
259     { ".asf",   "video/x-ms-asf" },
260     { ".m1a",   "audio/mpeg" },
261     { ".m2a",   "audio/mpeg" },
262     { ".m1v",   "video/mpeg" },
263     { ".m2v",   "video/mpeg" },
264     { ".mp2",   "audio/mpeg" },
265     { ".mp3",   "audio/mpeg" },
266     { ".mpa",   "audio/mpeg" },
267     { ".mpg",   "video/mpeg" },
268     { ".mpeg",  "video/mpeg" },
269     { ".mpe",   "video/mpeg" },
270     { ".mov",   "video/quicktime" },
271     { ".moov",  "video/quicktime" },
272     { ".ogg",   "application/ogg" },
273     { ".ogm",   "application/ogg" },
274     { ".wav",   "audio/wav" },
275     { ".wma",   "audio/x-ms-wma" },
276     { ".wmv",   "video/x-ms-wmv" },
277
278
279     /* end */
280     { NULL,     NULL }
281 };
282
283 static const char *httpd_MimeFromUrl( const char *psz_url )
284 {
285
286     char *psz_ext;
287
288     psz_ext = strrchr( psz_url, '.' );
289     if( psz_ext )
290     {
291         int i;
292
293         for( i = 0; http_mime[i].psz_ext != NULL ; i++ )
294         {
295             if( !strcasecmp( http_mime[i].psz_ext, psz_ext ) )
296             {
297                 return http_mime[i].psz_mime;
298             }
299         }
300     }
301     return "application/octet-stream";
302 }
303
304 /*****************************************************************************
305  * High Level Functions: httpd_file_t
306  *****************************************************************************/
307 struct httpd_file_t
308 {
309     httpd_url_t *url;
310
311     char *psz_url;
312     char *psz_mime;
313
314     httpd_file_callback_t pf_fill;
315     httpd_file_sys_t      *p_sys;
316
317 };
318
319 static int httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query )
320 {
321     httpd_file_t *file = (httpd_file_t*)p_sys;
322     uint8_t *psz_args = query->psz_args;
323     uint8_t **pp_body, *p_body;
324     int *pi_body, i_body;
325
326     if( answer == NULL || query == NULL )
327     {
328         return VLC_SUCCESS;
329     }
330     answer->i_proto  = HTTPD_PROTO_HTTP;
331     answer->i_version= query->i_version;
332     answer->i_type   = HTTPD_MSG_ANSWER;
333
334     answer->i_status = 200;
335     answer->psz_status = strdup( "OK" );
336
337     httpd_MsgAdd( answer, "Content-type",  "%s", file->psz_mime );
338     httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
339
340     if( query->i_type != HTTPD_MSG_HEAD )
341     {
342         pp_body = &answer->p_body;
343         pi_body = &answer->i_body;
344     }
345     else
346     {
347         /* The file still needs to be executed. */
348         p_body = NULL;
349         i_body = 0;
350         pp_body = &p_body;
351         pi_body = &i_body;
352     }
353
354     if( query->i_type == HTTPD_MSG_POST )
355     {
356         /* msg_Warn not supported */
357     }
358
359     file->pf_fill( file->p_sys, file, psz_args, pp_body, pi_body );
360
361     if( query->i_type == HTTPD_MSG_HEAD && p_body != NULL )
362     {
363         free( p_body );
364     }
365
366     /* We respect client request */
367     if( strcmp( httpd_MsgGet( &cl->query, "Connection" ), "" ) )
368     {
369         httpd_MsgAdd( answer, "Connection",
370                       httpd_MsgGet( &cl->query, "Connection" ) );
371     }
372
373     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
374
375     return VLC_SUCCESS;
376 }
377
378 httpd_file_t *httpd_FileNew( httpd_host_t *host,
379                              const char *psz_url, const char *psz_mime,
380                              const char *psz_user, const char *psz_password,
381                              const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill,
382                              httpd_file_sys_t *p_sys )
383 {
384     httpd_file_t *file = malloc( sizeof( httpd_file_t ) );
385
386     if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user,
387                                           psz_password, p_acl )
388         ) == NULL )
389     {
390         free( file );
391         return NULL;
392     }
393
394     file->psz_url  = strdup( psz_url );
395     if( psz_mime && *psz_mime )
396     {
397         file->psz_mime = strdup( psz_mime );
398     }
399     else
400     {
401         file->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
402     }
403
404     file->pf_fill = pf_fill;
405     file->p_sys   = p_sys;
406
407     httpd_UrlCatch( file->url, HTTPD_MSG_HEAD, httpd_FileCallBack,
408                     (httpd_callback_sys_t*)file );
409     httpd_UrlCatch( file->url, HTTPD_MSG_GET,  httpd_FileCallBack,
410                     (httpd_callback_sys_t*)file );
411     httpd_UrlCatch( file->url, HTTPD_MSG_POST, httpd_FileCallBack,
412                     (httpd_callback_sys_t*)file );
413
414     return file;
415 }
416
417 void httpd_FileDelete( httpd_file_t *file )
418 {
419     httpd_UrlDelete( file->url );
420
421     free( file->psz_url );
422     free( file->psz_mime );
423
424     free( file );
425 }
426
427 /*****************************************************************************
428  * High Level Functions: httpd_handler_t (for CGIs)
429  *****************************************************************************/
430 struct httpd_handler_t
431 {
432     httpd_url_t *url;
433
434     httpd_handler_callback_t pf_fill;
435     httpd_handler_sys_t      *p_sys;
436
437 };
438
439 static int httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query )
440 {
441     httpd_handler_t *handler = (httpd_handler_t*)p_sys;
442     uint8_t *psz_args = query->psz_args;
443     char psz_remote_addr[100];
444
445     if( answer == NULL || query == NULL )
446     {
447         return VLC_SUCCESS;
448     }
449     answer->i_proto  = HTTPD_PROTO_NONE;
450     answer->i_type   = HTTPD_MSG_ANSWER;
451
452     /* We do it ourselves, thanks */
453     answer->i_status = 0;
454     answer->psz_status = NULL;
455
456     switch( cl->sock.ss_family )
457     {
458 #ifdef HAVE_INET_PTON
459     case AF_INET:
460         inet_ntop( cl->sock.ss_family,
461                    &((struct sockaddr_in *)&cl->sock)->sin_addr,
462                    psz_remote_addr, sizeof(psz_remote_addr) );
463         break;
464     case AF_INET6:
465         inet_ntop( cl->sock.ss_family,
466                    &((struct sockaddr_in6 *)&cl->sock)->sin6_addr,
467                    psz_remote_addr, sizeof(psz_remote_addr) );
468         break;
469 #else
470     case AF_INET:
471     {
472         char *psz_tmp = inet_ntoa( ((struct sockaddr_in *)&cl->sock)->sin_addr );
473         strncpy( psz_remote_addr, psz_tmp, sizeof(psz_remote_addr) );
474         break;
475     }
476 #endif
477     default:
478         psz_remote_addr[0] = '\0';
479     }
480
481     handler->pf_fill( handler->p_sys, handler, query->psz_url, psz_args,
482                       query->i_type, query->p_body, query->i_body,
483                       psz_remote_addr, NULL,
484                       &answer->p_body, &answer->i_body );
485
486     if( query->i_type == HTTPD_MSG_HEAD )
487     {
488         char *p = (char *)answer->p_body;
489         while ( (p = strchr( p, '\r' )) != NULL )
490         {
491             if( p[1] && p[1] == '\n' && p[2] && p[2] == '\r'
492                  && p[3] && p[3] == '\n' )
493             {
494                 break;
495             }
496         }
497         if( p != NULL )
498         {
499             p[4] = '\0';
500             answer->i_body = strlen((char*)answer->p_body) + 1;
501             answer->p_body = realloc( answer->p_body, answer->i_body );
502         }
503     }
504
505     if( strncmp( (char *)answer->p_body, "HTTP/1.", 7 ) )
506     {
507         int i_status, i_headers;
508         char *psz_headers, *psz_new, *psz_status;
509
510         if( !strncmp( (char *)answer->p_body, "Status: ", 8 ) )
511         {
512             /* Apache-style */
513             i_status = strtol( (char *)&answer->p_body[8], &psz_headers, 0 );
514             if( *psz_headers ) psz_headers++;
515             if( *psz_headers ) psz_headers++;
516             i_headers = answer->i_body - (psz_headers - (char *)answer->p_body);
517         }
518         else
519         {
520             i_status = 200;
521             psz_headers = (char *)answer->p_body;
522             i_headers = answer->i_body;
523         }
524         switch( i_status )
525         {
526         case 200:
527             psz_status = "OK";
528             break;
529         case 401:
530             psz_status = "Unauthorized";
531             break;
532         default:
533             if( (i_status < 0) || (i_status > 999) )
534                 i_status = 500;
535             psz_status = "Undefined";
536             break;
537         }
538         answer->i_body = sizeof("HTTP/1.0 xxx \r\n")
539                         + strlen(psz_status) + i_headers - 1;
540         psz_new = (char *)malloc( answer->i_body + 1);
541         sprintf( psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status );
542         memcpy( &psz_new[strlen(psz_new)], psz_headers, i_headers );
543         free( answer->p_body );
544         answer->p_body = (uint8_t *)psz_new;
545     }
546
547     return VLC_SUCCESS;
548 }
549
550 httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
551                                    const char *psz_user,
552                                    const char *psz_password,
553                                    const vlc_acl_t *p_acl,
554                                    httpd_handler_callback_t pf_fill,
555                                    httpd_handler_sys_t *p_sys )
556 {
557     httpd_handler_t *handler = malloc( sizeof( httpd_handler_t ) );
558
559     if( ( handler->url = httpd_UrlNewUnique( host, psz_url, psz_user,
560                                              psz_password, p_acl )
561         ) == NULL )
562     {
563         free( handler );
564         return NULL;
565     }
566
567     handler->pf_fill = pf_fill;
568     handler->p_sys   = p_sys;
569
570     httpd_UrlCatch( handler->url, HTTPD_MSG_HEAD, httpd_HandlerCallBack,
571                     (httpd_callback_sys_t*)handler );
572     httpd_UrlCatch( handler->url, HTTPD_MSG_GET,  httpd_HandlerCallBack,
573                     (httpd_callback_sys_t*)handler );
574     httpd_UrlCatch( handler->url, HTTPD_MSG_POST, httpd_HandlerCallBack,
575                     (httpd_callback_sys_t*)handler );
576
577     return handler;
578 }
579
580 void httpd_HandlerDelete( httpd_handler_t *handler )
581 {
582     httpd_UrlDelete( handler->url );
583     free( handler );
584 }
585
586 /*****************************************************************************
587  * High Level Functions: httpd_redirect_t
588  *****************************************************************************/
589 struct httpd_redirect_t
590 {
591     httpd_url_t *url;
592     char        *psz_dst;
593 };
594
595 static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys,
596                                    httpd_client_t *cl, httpd_message_t *answer,
597                                    httpd_message_t *query )
598 {
599     httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys;
600     uint8_t *p;
601
602     if( answer == NULL || query == NULL )
603     {
604         return VLC_SUCCESS;
605     }
606     answer->i_proto  = query->i_proto;
607     answer->i_version= query->i_version;
608     answer->i_type   = HTTPD_MSG_ANSWER;
609     answer->i_status = 301;
610     answer->psz_status = strdup( "Moved Permanently" );
611
612     p = answer->p_body = malloc( 1000 + strlen( rdir->psz_dst ) );
613     p += sprintf( (char *)p,
614         "<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
615         "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
616         "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
617         "<html>\n"
618         "<head>\n"
619         "<title>Redirection</title>\n"
620         "</head>\n"
621         "<body>\n"
622         "<h1>You should be " 
623         "<a href=\"%s\">redirected</a></h1>\n"
624         "<hr />\n"
625         "<p><a href=\"http://www.videolan.org\">VideoLAN</a>\n</p>"
626         "<hr />\n"
627         "</body>\n"
628         "</html>\n", rdir->psz_dst );
629     answer->i_body = p - answer->p_body;
630
631     /* XXX check if it's ok or we need to set an absolute url */
632     httpd_MsgAdd( answer, "Location",  "%s", rdir->psz_dst );
633
634     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
635
636     return VLC_SUCCESS;
637 }
638
639 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst,
640                                      const char *psz_url_src )
641 {
642     httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) );
643
644     if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL, NULL ) ) )
645     {
646         free( rdir );
647         return NULL;
648     }
649     rdir->psz_dst = strdup( psz_url_dst );
650
651     /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
652     httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack,
653                     (httpd_callback_sys_t*)rdir );
654     httpd_UrlCatch( rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack,
655                     (httpd_callback_sys_t*)rdir );
656     httpd_UrlCatch( rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack,
657                     (httpd_callback_sys_t*)rdir );
658     httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack,
659                     (httpd_callback_sys_t*)rdir );
660
661     return rdir;
662 }
663 void httpd_RedirectDelete( httpd_redirect_t *rdir )
664 {
665     httpd_UrlDelete( rdir->url );
666     free( rdir->psz_dst );
667     free( rdir );
668 }
669
670 /*****************************************************************************
671  * High Level Funtions: httpd_stream_t
672  *****************************************************************************/
673 struct httpd_stream_t
674 {
675     vlc_mutex_t lock;
676     httpd_url_t *url;
677
678     char    *psz_mime;
679
680     /* Header to send as first packet */
681     uint8_t *p_header;
682     int     i_header;
683
684     /* circular buffer */
685     int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
686     uint8_t     *p_buffer;          /* buffer */
687     int64_t     i_buffer_pos;       /* absolute position from begining */
688     int64_t     i_buffer_last_pos;  /* a new connection will start with that */
689 };
690
691 static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
692                                  httpd_client_t *cl, httpd_message_t *answer,
693                                  httpd_message_t *query )
694 {
695     httpd_stream_t *stream = (httpd_stream_t*)p_sys;
696
697     if( answer == NULL || query == NULL || cl == NULL )
698     {
699         return VLC_SUCCESS;
700     }
701
702     if( answer->i_body_offset > 0 )
703     {
704         int64_t i_write;
705         int     i_pos;
706
707 #if 0
708         fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n",
709                  answer->i_body_offset );
710 #endif
711
712         if( answer->i_body_offset >= stream->i_buffer_pos )
713         {
714             /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */
715             return VLC_EGENERIC;    /* wait, no data available */
716         }
717         if( answer->i_body_offset + stream->i_buffer_size <
718             stream->i_buffer_pos )
719         {
720             /* this client isn't fast enough */
721             fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n",
722                      answer->i_body_offset, stream->i_buffer_last_pos );
723             answer->i_body_offset = stream->i_buffer_last_pos;
724         }
725
726         i_pos   = answer->i_body_offset % stream->i_buffer_size;
727         i_write = stream->i_buffer_pos - answer->i_body_offset;
728         if( i_write > HTTPD_CL_BUFSIZE )
729         {
730             i_write = HTTPD_CL_BUFSIZE;
731         }
732         else if( i_write <= 0 )
733         {
734             return VLC_EGENERIC;    /* wait, no data available */
735         }
736
737         /* Don't go past the end of the circular buffer */
738         i_write = __MIN( i_write, stream->i_buffer_size - i_pos );
739
740         /* using HTTPD_MSG_ANSWER -> data available */
741         answer->i_proto  = HTTPD_PROTO_HTTP;
742         answer->i_version= 0;
743         answer->i_type   = HTTPD_MSG_ANSWER;
744
745         answer->i_body = i_write;
746         answer->p_body = malloc( i_write );
747         memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write );
748
749         answer->i_body_offset += i_write;
750
751         return VLC_SUCCESS;
752     }
753     else
754     {
755         answer->i_proto  = HTTPD_PROTO_HTTP;
756         answer->i_version= 0;
757         answer->i_type   = HTTPD_MSG_ANSWER;
758
759         answer->i_status = 200;
760         answer->psz_status = strdup( "OK" );
761
762         if( query->i_type != HTTPD_MSG_HEAD )
763         {
764             httpd_ClientModeStream( cl );
765             vlc_mutex_lock( &stream->lock );
766             /* Send the header */
767             if( stream->i_header > 0 )
768             {
769                 answer->i_body = stream->i_header;
770                 answer->p_body = malloc( stream->i_header );
771                 memcpy( answer->p_body, stream->p_header, stream->i_header );
772             }
773             answer->i_body_offset = stream->i_buffer_last_pos;
774             vlc_mutex_unlock( &stream->lock );
775         }
776         else
777         {
778             httpd_MsgAdd( answer, "Content-Length", "%d", 0 );
779             answer->i_body_offset = 0;
780         }
781
782         if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) )
783         {
784             vlc_bool_t b_xplaystream = VLC_FALSE;
785             int i;
786
787             httpd_MsgAdd( answer, "Content-type", "%s",
788                           "application/octet-stream" );
789             httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" );
790             httpd_MsgAdd( answer, "Pragma", "no-cache" );
791             httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff );
792             httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" );
793
794             /* Check if there is a xPlayStrm=1 */
795             for( i = 0; i < query->i_name; i++ )
796             {
797                 if( !strcasecmp( query->name[i],  "Pragma" ) &&
798                     strstr( query->value[i], "xPlayStrm=1" ) )
799                 {
800                     b_xplaystream = VLC_TRUE;
801                 }
802             }
803
804             if( !b_xplaystream )
805             {
806                 answer->i_body_offset = 0;
807             }
808         }
809         else
810         {
811             httpd_MsgAdd( answer, "Content-type",  "%s", stream->psz_mime );
812         }
813         httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
814         return VLC_SUCCESS;
815     }
816 }
817
818 httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
819                                  const char *psz_url, const char *psz_mime,
820                                  const char *psz_user, const char *psz_password,
821                                  const vlc_acl_t *p_acl )
822 {
823     httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) );
824
825     if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user,
826                                             psz_password, p_acl )
827         ) == NULL )
828     {
829         free( stream );
830         return NULL;
831     }
832     vlc_mutex_init( host, &stream->lock );
833     if( psz_mime && *psz_mime )
834     {
835         stream->psz_mime = strdup( psz_mime );
836     }
837     else
838     {
839         stream->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
840     }
841     stream->i_header = 0;
842     stream->p_header = NULL;
843     stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
844     stream->p_buffer = malloc( stream->i_buffer_size );
845     /* We set to 1 to make life simpler
846      * (this way i_body_offset can never be 0) */
847     stream->i_buffer_pos = 1;
848     stream->i_buffer_last_pos = 1;
849
850     httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack,
851                     (httpd_callback_sys_t*)stream );
852     httpd_UrlCatch( stream->url, HTTPD_MSG_GET, httpd_StreamCallBack,
853                     (httpd_callback_sys_t*)stream );
854     httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack,
855                     (httpd_callback_sys_t*)stream );
856
857     return stream;
858 }
859
860 int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data )
861 {
862     vlc_mutex_lock( &stream->lock );
863     if( stream->p_header )
864     {
865         free( stream->p_header );
866         stream->p_header = NULL;
867     }
868     stream->i_header = i_data;
869     if( i_data > 0 )
870     {
871         stream->p_header = malloc( i_data );
872         memcpy( stream->p_header, p_data, i_data );
873     }
874     vlc_mutex_unlock( &stream->lock );
875
876     return VLC_SUCCESS;
877 }
878
879 int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data )
880 {
881     int i_count;
882     int i_pos;
883
884     if( i_data < 0 || p_data == NULL )
885     {
886         return VLC_SUCCESS;
887     }
888     vlc_mutex_lock( &stream->lock );
889
890     /* save this pointer (to be used by new connection) */
891     stream->i_buffer_last_pos = stream->i_buffer_pos;
892
893     i_pos = stream->i_buffer_pos % stream->i_buffer_size;
894     i_count = i_data;
895     while( i_count > 0)
896     {
897         int i_copy;
898
899         i_copy = __MIN( i_count, stream->i_buffer_size - i_pos );
900
901         /* Ok, we can't go past the end of our buffer */
902         memcpy( &stream->p_buffer[i_pos], p_data, i_copy );
903
904         i_pos = ( i_pos + i_copy ) % stream->i_buffer_size;
905         i_count -= i_copy;
906         p_data  += i_copy;
907     }
908
909     stream->i_buffer_pos += i_data;
910
911     vlc_mutex_unlock( &stream->lock );
912     return VLC_SUCCESS;
913 }
914
915 void httpd_StreamDelete( httpd_stream_t *stream )
916 {
917     httpd_UrlDelete( stream->url );
918     vlc_mutex_destroy( &stream->lock );
919     if( stream->psz_mime ) free( stream->psz_mime );
920     if( stream->p_header ) free( stream->p_header );
921     if( stream->p_buffer ) free( stream->p_buffer );
922     free( stream );
923 }
924
925 /*****************************************************************************
926  * Low level
927  *****************************************************************************/
928 static void httpd_HostThread( httpd_host_t * );
929
930 /* create a new host */
931 httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host,
932                              int i_port )
933 {
934     return httpd_TLSHostNew( p_this, psz_host, i_port, NULL, NULL, NULL, NULL
935                            );
936 }
937
938 httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
939                                 int i_port,
940                                 const char *psz_cert, const char *psz_key,
941                                 const char *psz_ca, const char *psz_crl )
942 {
943     httpd_t      *httpd;
944     httpd_host_t *host;
945     tls_server_t *p_tls;
946     char *psz_host;
947     vlc_value_t  lockval;
948     int i;
949
950     if( psz_hostname == NULL )
951         psz_hostname = "";
952
953     psz_host = strdup( psz_hostname );
954     if( psz_host == NULL )
955     {
956         msg_Err( p_this, "memory error" );
957         return NULL;
958     }
959
960     /* to be sure to avoid multiple creation */
961     var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX );
962     var_Get( p_this->p_libvlc, "httpd_mutex", &lockval );
963     vlc_mutex_lock( lockval.p_address );
964
965     if( !(httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE )) )
966     {
967         msg_Info( p_this, "creating httpd" );
968         if( ( httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ) ) == NULL )
969         {
970             vlc_mutex_unlock( lockval.p_address );
971             free( psz_host );
972             return NULL;
973         }
974
975         httpd->i_host = 0;
976         httpd->host   = NULL;
977
978         vlc_object_yield( httpd );
979         vlc_object_attach( httpd, p_this->p_vlc );
980     }
981
982     /* verify if it already exist */
983     for( i = httpd->i_host - 1; i >= 0; i-- )
984     {
985         host = httpd->host[i];
986
987         /* cannot mix TLS and non-TLS hosts */
988         if( ( ( httpd->host[i]->p_tls != NULL ) != ( psz_cert != NULL ) )
989          || ( host->i_port != i_port )
990          || strcmp( host->psz_hostname, psz_hostname ) )
991             continue;
992
993         /* yep found */
994         host->i_ref++;
995
996         vlc_mutex_unlock( lockval.p_address );
997         return host;
998     }
999
1000     host = NULL;
1001
1002     /* determine TLS configuration */
1003     if ( psz_cert != NULL )
1004     {
1005         p_tls = tls_ServerCreate( p_this, psz_cert, psz_key );
1006         if ( p_tls == NULL )
1007         {
1008             msg_Err( p_this, "TLS initialization error" );
1009             goto error;
1010         }
1011
1012         if ( ( psz_ca != NULL) && tls_ServerAddCA( p_tls, psz_ca ) )
1013         {
1014             msg_Err( p_this, "TLS CA error" );
1015             goto error;
1016         }
1017
1018         if ( ( psz_crl != NULL) && tls_ServerAddCRL( p_tls, psz_crl ) )
1019         {
1020             msg_Err( p_this, "TLS CRL error" );
1021             goto error;
1022         }
1023     }
1024     else
1025         p_tls = NULL;
1026
1027     /* create the new host */
1028     host = vlc_object_create( p_this, sizeof( httpd_host_t ) );
1029     host->httpd = httpd;
1030     vlc_mutex_init( httpd, &host->lock );
1031     host->i_ref = 1;
1032
1033     host->fd = net_ListenTCP( p_this, psz_host, i_port );
1034     if( host->fd == NULL )
1035     {
1036         msg_Err( p_this, "cannot create socket(s) for HTTP host" );
1037         goto error;
1038     }
1039
1040     host->i_port = i_port;
1041     host->psz_hostname = psz_host;
1042
1043     host->i_url     = 0;
1044     host->url       = NULL;
1045     host->i_client  = 0;
1046     host->client    = NULL;
1047
1048     host->p_tls = p_tls;
1049
1050     /* create the thread */
1051     if( vlc_thread_create( host, "httpd host thread", httpd_HostThread,
1052                            VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
1053     {
1054         msg_Err( p_this, "cannot spawn http host thread" );
1055         goto error;
1056     }
1057
1058     /* now add it to httpd */
1059     TAB_APPEND( httpd->i_host, httpd->host, host );
1060     vlc_mutex_unlock( lockval.p_address );
1061
1062     return host;
1063
1064 error:
1065     free( psz_host );
1066     if( httpd->i_host <= 0 )
1067     {
1068         vlc_object_release( httpd );
1069         vlc_object_detach( httpd );
1070         vlc_object_destroy( httpd );
1071     }
1072     vlc_mutex_unlock( lockval.p_address );
1073
1074     if( host != NULL )
1075     {
1076         net_ListenClose( host->fd );
1077         vlc_mutex_destroy( &host->lock );
1078         vlc_object_destroy( host );
1079     }
1080
1081     if( p_tls != NULL )
1082         tls_ServerDelete( p_tls );
1083
1084     return NULL;
1085 }
1086
1087 /* delete a host */
1088 void httpd_HostDelete( httpd_host_t *host )
1089 {
1090     httpd_t *httpd = host->httpd;
1091     vlc_value_t lockval;
1092     int i;
1093
1094     var_Get( httpd->p_libvlc, "httpd_mutex", &lockval );
1095     vlc_mutex_lock( lockval.p_address );
1096
1097     host->i_ref--;
1098     if( host->i_ref > 0 )
1099     {
1100         /* still used */
1101         vlc_mutex_unlock( lockval.p_address );
1102         msg_Dbg( host, "httpd_HostDelete: host still used" );
1103         return;
1104     }
1105     TAB_REMOVE( httpd->i_host, httpd->host, host );
1106
1107     host->b_die = 1;
1108     vlc_thread_join( host );
1109
1110     msg_Dbg( host, "HTTP host removed" );
1111
1112     for( i = 0; i < host->i_url; i++ )
1113     {
1114         msg_Err( host, "url still registered:%s", host->url[i]->psz_url );
1115     }
1116     for( i = 0; i < host->i_client; i++ )
1117     {
1118         httpd_client_t *cl = host->client[i];
1119         msg_Warn( host, "client still connected" );
1120         httpd_ClientClean( cl );
1121         TAB_REMOVE( host->i_client, host->client, cl );
1122         free( cl );
1123         i--;
1124         /* TODO */
1125     }
1126
1127     if( host->p_tls != NULL)
1128         tls_ServerDelete( host->p_tls );
1129
1130     net_ListenClose( host->fd );
1131     free( host->psz_hostname );
1132
1133     vlc_mutex_destroy( &host->lock );
1134     vlc_object_destroy( host );
1135
1136     vlc_object_release( httpd );
1137     if( httpd->i_host <= 0 )
1138     {
1139         msg_Dbg( httpd, "no host left, stopping httpd" );
1140         vlc_object_detach( httpd );
1141         vlc_object_destroy( httpd );
1142     }
1143     vlc_mutex_unlock( lockval.p_address );
1144 }
1145
1146 /* register a new url */
1147 static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url,
1148                                          const char *psz_user, const char *psz_password,
1149                                          const vlc_acl_t *p_acl, vlc_bool_t b_check )
1150 {
1151     httpd_url_t *url;
1152     int         i;
1153
1154     vlc_mutex_lock( &host->lock );
1155     if( b_check )
1156     {
1157         for( i = 0; i < host->i_url; i++ )
1158         {
1159             if( !strcmp( psz_url, host->url[i]->psz_url ) )
1160             {
1161                 msg_Warn( host->httpd,
1162                           "cannot add '%s' (url already defined)", psz_url );
1163                 vlc_mutex_unlock( &host->lock );
1164                 return NULL;
1165             }
1166         }
1167     }
1168
1169     url = malloc( sizeof( httpd_url_t ) );
1170     url->host = host;
1171
1172     vlc_mutex_init( host->httpd, &url->lock );
1173     url->psz_url = strdup( psz_url );
1174     url->psz_user = strdup( psz_user ? psz_user : "" );
1175     url->psz_password = strdup( psz_password ? psz_password : "" );
1176     url->p_acl = ACL_Duplicate( host, p_acl );
1177     for( i = 0; i < HTTPD_MSG_MAX; i++ )
1178     {
1179         url->catch[i].cb = NULL;
1180         url->catch[i].p_sys = NULL;
1181     }
1182
1183     TAB_APPEND( host->i_url, host->url, url );
1184     vlc_mutex_unlock( &host->lock );
1185
1186     return url;
1187 }
1188
1189 httpd_url_t *httpd_UrlNew( httpd_host_t *host, const char *psz_url,
1190                            const char *psz_user, const char *psz_password,
1191                            const vlc_acl_t *p_acl )
1192 {
1193     return httpd_UrlNewPrivate( host, psz_url, psz_user,
1194                                 psz_password, p_acl, VLC_FALSE );
1195 }
1196
1197 httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, const char *psz_url,
1198                                  const char *psz_user, const char *psz_password,
1199                                  const vlc_acl_t *p_acl )
1200 {
1201     return httpd_UrlNewPrivate( host, psz_url, psz_user,
1202                                 psz_password, p_acl, VLC_TRUE );
1203 }
1204
1205 /* register callback on a url */
1206 int httpd_UrlCatch( httpd_url_t *url, int i_msg, httpd_callback_t cb,
1207                     httpd_callback_sys_t *p_sys )
1208 {
1209     vlc_mutex_lock( &url->lock );
1210     url->catch[i_msg].cb   = cb;
1211     url->catch[i_msg].p_sys= p_sys;
1212     vlc_mutex_unlock( &url->lock );
1213
1214     return VLC_SUCCESS;
1215 }
1216
1217 /* delete an url */
1218 void httpd_UrlDelete( httpd_url_t *url )
1219 {
1220     httpd_host_t *host = url->host;
1221     int          i;
1222
1223     vlc_mutex_lock( &host->lock );
1224     TAB_REMOVE( host->i_url, host->url, url );
1225
1226     vlc_mutex_destroy( &url->lock );
1227     free( url->psz_url );
1228     free( url->psz_user );
1229     free( url->psz_password );
1230     ACL_Destroy( url->p_acl );
1231
1232     for( i = 0; i < host->i_client; i++ )
1233     {
1234         httpd_client_t *client = host->client[i];
1235
1236         if( client->url == url )
1237         {
1238             /* TODO complete it */
1239             msg_Warn( host, "force closing connections" );
1240             httpd_ClientClean( client );
1241             TAB_REMOVE( host->i_client, host->client, client );
1242             free( client );
1243             i--;
1244         }
1245     }
1246     free( url );
1247     vlc_mutex_unlock( &host->lock );
1248 }
1249
1250 void httpd_MsgInit( httpd_message_t *msg )
1251 {
1252     msg->cl         = NULL;
1253     msg->i_type     = HTTPD_MSG_NONE;
1254     msg->i_proto    = HTTPD_PROTO_NONE;
1255     msg->i_version  = -1;
1256
1257     msg->i_status   = 0;
1258     msg->psz_status = NULL;
1259
1260     msg->psz_url    = NULL;
1261     msg->psz_args   = NULL;
1262
1263     msg->i_channel  = -1;
1264
1265     msg->i_name     = 0;
1266     msg->name       = NULL;
1267     msg->i_value    = 0;
1268     msg->value      = NULL;
1269
1270     msg->i_body_offset = 0;
1271     msg->i_body        = 0;
1272     msg->p_body        = 0;
1273 }
1274
1275 void httpd_MsgClean( httpd_message_t *msg )
1276 {
1277     int i;
1278
1279     if( msg->psz_status )
1280     {
1281         free( msg->psz_status );
1282     }
1283     if( msg->psz_url )
1284     {
1285         free( msg->psz_url );
1286     }
1287     if( msg->psz_args )
1288     {
1289         free( msg->psz_args );
1290     }
1291     for( i = 0; i < msg->i_name; i++ )
1292     {
1293         free( msg->name[i] );
1294         free( msg->value[i] );
1295     }
1296     if( msg->name )
1297     {
1298         free( msg->name );
1299     }
1300     if( msg->value )
1301     {
1302         free( msg->value );
1303     }
1304     if( msg->p_body )
1305     {
1306         free( msg->p_body );
1307     }
1308     httpd_MsgInit( msg );
1309 }
1310
1311 char *httpd_MsgGet( httpd_message_t *msg, char *name )
1312 {
1313     int i;
1314
1315     for( i = 0; i < msg->i_name; i++ )
1316     {
1317         if( !strcasecmp( msg->name[i], name ))
1318         {
1319             return msg->value[i];
1320         }
1321     }
1322     return "";
1323 }
1324
1325 void httpd_MsgAdd( httpd_message_t *msg, char *name, char *psz_value, ... )
1326 {
1327     va_list args;
1328     char *value = NULL;
1329
1330     va_start( args, psz_value );
1331 #if defined(HAVE_VASPRINTF) && !defined(__APPLE__) && !defined(SYS_BEOS)
1332     vasprintf( &value, psz_value, args );
1333 #else
1334     {
1335         int i_size = strlen( psz_value ) + 4096;    /* FIXME stupid system */
1336         value = calloc( i_size, sizeof( char ) );
1337         vsnprintf( value, i_size, psz_value, args );
1338         value[i_size - 1] = 0;
1339     }
1340 #endif
1341     va_end( args );
1342
1343     name = strdup( name );
1344
1345     TAB_APPEND( msg->i_name,  msg->name,  name );
1346     TAB_APPEND( msg->i_value, msg->value, value );
1347 }
1348
1349 static void httpd_ClientInit( httpd_client_t *cl )
1350 {
1351     cl->i_state = HTTPD_CLIENT_RECEIVING;
1352     cl->i_activity_date = mdate();
1353     cl->i_activity_timeout = I64C(10000000);
1354     cl->i_buffer_size = HTTPD_CL_BUFSIZE;
1355     cl->i_buffer = 0;
1356     cl->p_buffer = malloc( cl->i_buffer_size );
1357     cl->i_mode   = HTTPD_CLIENT_FILE;
1358     cl->b_read_waiting = VLC_FALSE;
1359
1360     httpd_MsgInit( &cl->query );
1361     httpd_MsgInit( &cl->answer );
1362 }
1363
1364 void httpd_ClientModeStream( httpd_client_t *cl )
1365 {
1366     cl->i_mode   = HTTPD_CLIENT_STREAM;
1367 }
1368
1369 void httpd_ClientModeBidir( httpd_client_t *cl )
1370 {
1371     cl->i_mode   = HTTPD_CLIENT_BIDIR;
1372 }
1373
1374 char* httpd_ClientIP( httpd_client_t *cl, char *psz_ip )
1375 {
1376     return net_GetPeerAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip;
1377 }
1378
1379 char* httpd_ServerIP( httpd_client_t *cl, char *psz_ip )
1380 {
1381     return net_GetSockAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip;
1382 }
1383
1384 static void httpd_ClientClean( httpd_client_t *cl )
1385 {
1386     if( cl->fd >= 0 )
1387     {
1388         if( cl->p_tls != NULL )
1389             tls_ServerSessionClose( cl->p_tls );
1390         net_Close( cl->fd );
1391         cl->fd = -1;
1392     }
1393
1394     httpd_MsgClean( &cl->answer );
1395     httpd_MsgClean( &cl->query );
1396
1397     if( cl->p_buffer )
1398     {
1399         free( cl->p_buffer );
1400         cl->p_buffer = NULL;
1401     }
1402 }
1403
1404 static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock,
1405                                         int i_sock_size,
1406                                         tls_session_t *p_tls )
1407 {
1408     httpd_client_t *cl = malloc( sizeof( httpd_client_t ) );
1409
1410     if( !cl ) return NULL;
1411
1412     cl->i_ref   = 0;
1413     cl->fd      = fd;
1414     memcpy( &cl->sock, sock, sizeof( cl->sock ) );
1415     cl->i_sock_size = i_sock_size;
1416     cl->url     = NULL;
1417     cl->p_tls = p_tls;
1418
1419     httpd_ClientInit( cl );
1420
1421     return cl;
1422 }
1423
1424 static int httpd_NetRecv( httpd_client_t *cl, uint8_t *p, int i_len )
1425 {
1426     tls_session_t *p_tls;
1427
1428     p_tls = cl->p_tls;
1429     if( p_tls != NULL)
1430         return tls_Recv( p_tls, p, i_len );
1431
1432     return recv( cl->fd, p, i_len, 0 );
1433 }
1434
1435 static int httpd_NetSend( httpd_client_t *cl, const uint8_t *p, int i_len )
1436 {
1437     tls_session_t *p_tls;
1438
1439     p_tls = cl->p_tls;
1440     if( p_tls != NULL)
1441         return tls_Send( p_tls, p, i_len );
1442
1443     return send( cl->fd, p, i_len, 0 );
1444 }
1445
1446 static void httpd_ClientRecv( httpd_client_t *cl )
1447 {
1448     int i_len;
1449
1450     if( cl->query.i_proto == HTTPD_PROTO_NONE )
1451     {
1452         /* enough to see if it's rtp over rtsp or RTSP/HTTP */
1453         i_len = httpd_NetRecv( cl, &cl->p_buffer[cl->i_buffer],
1454                                4 - cl->i_buffer );
1455         if( i_len > 0 )
1456         {
1457             cl->i_buffer += i_len;
1458         }
1459
1460         if( cl->i_buffer >= 4 )
1461         {
1462             /*fprintf( stderr, "peek=%4.4s\n", cl->p_buffer );*/
1463             /* detect type */
1464             if( cl->p_buffer[0] == '$' )
1465             {
1466                 /* RTSP (rtp over rtsp) */
1467                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1468                 cl->query.i_type  = HTTPD_MSG_CHANNEL;
1469                 cl->query.i_channel = cl->p_buffer[1];
1470                 cl->query.i_body  = (cl->p_buffer[2] << 8)|cl->p_buffer[3];
1471                 cl->query.p_body  = malloc( cl->query.i_body );
1472
1473                 cl->i_buffer      = 0;
1474             }
1475             else if( !memcmp( cl->p_buffer, "HTTP", 4 ) )
1476             {
1477                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1478                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1479             }
1480             else if( !memcmp( cl->p_buffer, "RTSP", 4 ) )
1481             {
1482                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1483                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1484             }
1485             else if( !memcmp( cl->p_buffer, "GET", 3 ) ||
1486                      !memcmp( cl->p_buffer, "HEAD", 4 ) ||
1487                      !memcmp( cl->p_buffer, "POST", 4 ) )
1488             {
1489                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1490                 cl->query.i_type  = HTTPD_MSG_NONE;
1491             }
1492             else
1493             {
1494                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1495                 cl->query.i_type  = HTTPD_MSG_NONE;
1496             }
1497         }
1498     }
1499     else if( cl->query.i_body > 0 )
1500     {
1501         /* we are reading the body of a request or a channel */
1502         i_len = httpd_NetRecv( cl, &cl->query.p_body[cl->i_buffer],
1503                                cl->query.i_body - cl->i_buffer );
1504         if( i_len > 0 )
1505         {
1506             cl->i_buffer += i_len;
1507         }
1508         if( cl->i_buffer >= cl->query.i_body )
1509         {
1510             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1511         }
1512     }
1513     else
1514     {
1515         /* we are reading a header -> char by char */
1516         for( ;; )
1517         {
1518             i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1 );
1519             if( i_len <= 0 )
1520             {
1521                 break;
1522             }
1523             cl->i_buffer++;
1524
1525             if( cl->i_buffer + 1 >= cl->i_buffer_size )
1526             {
1527                 cl->i_buffer_size += 1024;
1528                 cl->p_buffer = realloc( cl->p_buffer, cl->i_buffer_size );
1529             }
1530             if( ( cl->i_buffer >= 2 && !memcmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )||
1531                 ( cl->i_buffer >= 4 && !memcmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) )
1532             {
1533                 char *p;
1534
1535                 /* we have finished the header so parse it and set i_body */
1536                 cl->p_buffer[cl->i_buffer] = '\0';
1537
1538                 if( cl->query.i_type == HTTPD_MSG_ANSWER )
1539                 {
1540                     /* FIXME:
1541                      * assume strlen( "HTTP/1.x" ) = 8
1542                      */
1543                     cl->query.i_status =
1544                         strtol( (char *)&cl->p_buffer[8],
1545                                 &p, 0 );
1546                     while( *p == ' ' )
1547                     {
1548                         p++;
1549                     }
1550                     cl->query.psz_status = strdup( p );
1551                 }
1552                 else
1553                 {
1554                     static const struct
1555                     {
1556                         char *name;
1557                         int  i_type;
1558                         int  i_proto;
1559                     }
1560                     msg_type[] =
1561                     {
1562                         { "GET",        HTTPD_MSG_GET,  HTTPD_PROTO_HTTP },
1563                         { "HEAD",       HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP },
1564                         { "POST",       HTTPD_MSG_POST, HTTPD_PROTO_HTTP },
1565
1566                         { "OPTIONS",    HTTPD_MSG_OPTIONS,  HTTPD_PROTO_RTSP },
1567                         { "DESCRIBE",   HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP },
1568                         { "SETUP",      HTTPD_MSG_SETUP,    HTTPD_PROTO_RTSP },
1569                         { "PLAY",       HTTPD_MSG_PLAY,     HTTPD_PROTO_RTSP },
1570                         { "PAUSE",      HTTPD_MSG_PAUSE,    HTTPD_PROTO_RTSP },
1571                         { "TEARDOWN",   HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP },
1572
1573                         { NULL,         HTTPD_MSG_NONE,     HTTPD_PROTO_NONE }
1574                     };
1575                     int  i;
1576
1577                     p = NULL;
1578                     cl->query.i_type = HTTPD_MSG_NONE;
1579
1580                     /*fprintf( stderr, "received new request=%s\n", cl->p_buffer);*/
1581
1582                     for( i = 0; msg_type[i].name != NULL; i++ )
1583                     {
1584                         if( !strncmp( (char *)cl->p_buffer, msg_type[i].name,
1585                                       strlen( msg_type[i].name ) ) )
1586                         {
1587                             p = (char *)&cl->p_buffer[strlen((char *)msg_type[i].name) + 1 ];
1588                             cl->query.i_type = msg_type[i].i_type;
1589                             if( cl->query.i_proto != msg_type[i].i_proto )
1590                             {
1591                                 p = NULL;
1592                                 cl->query.i_proto = HTTPD_PROTO_NONE;
1593                                 cl->query.i_type = HTTPD_MSG_NONE;
1594                             }
1595                             break;
1596                         }
1597                     }
1598                     if( p == NULL )
1599                     {
1600                         if( strstr( (char *)cl->p_buffer, "HTTP/1." ) )
1601                         {
1602                             cl->query.i_proto = HTTPD_PROTO_HTTP;
1603                         }
1604                         else if( strstr( (char *)cl->p_buffer, "RTSP/1." ) )
1605                         {
1606                             cl->query.i_proto = HTTPD_PROTO_RTSP;
1607                         }
1608                     }
1609                     else
1610                     {
1611                         char *p2;
1612                         char *p3;
1613
1614                         while( *p == ' ' )
1615                         {
1616                             p++;
1617                         }
1618                         p2 = strchr( p, ' ' );
1619                         if( p2 )
1620                         {
1621                             *p2++ = '\0';
1622                         }
1623                         if( !strncasecmp( p, "rtsp:", 5 ) )
1624                         {
1625                             /* for rtsp url, you have rtsp://localhost:port/path */
1626                             p += 5;
1627                             while( *p == '/' ) p++;
1628                             while( *p && *p != '/' ) p++;
1629                         }
1630                         cl->query.psz_url = strdup( p );
1631                         if( ( p3 = strchr( cl->query.psz_url, '?' ) )  )
1632                         {
1633                             *p3++ = '\0';
1634                             cl->query.psz_args = (uint8_t *)strdup( p3 );
1635                         }
1636                         if( p2 )
1637                         {
1638                             while( *p2 == ' ' )
1639                             {
1640                                 p2++;
1641                             }
1642                             if( !strncasecmp( p2, "HTTP/1.", 7 ) )
1643                             {
1644                                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1645                                 cl->query.i_version = atoi( p2+7 );
1646                             }
1647                             else if( !strncasecmp( p2, "RTSP/1.", 7 ) )
1648                             {
1649                                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1650                                 cl->query.i_version = atoi( p2+7 );
1651                             }
1652                         }
1653                         p = p2;
1654                     }
1655                 }
1656                 if( p )
1657                 {
1658                     p = strchr( p, '\n' );
1659                 }
1660                 if( p )
1661                 {
1662                     while( *p == '\n' || *p == '\r' )
1663                     {
1664                         p++;
1665                     }
1666                     while( p && *p != '\0' )
1667                     {
1668                         char *line = p;
1669                         char *eol = p = strchr( p, '\n' );
1670                         char *colon;
1671
1672                         while( eol && eol >= line && ( *eol == '\n' || *eol == '\r' ) )
1673                         {
1674                             *eol-- = '\0';
1675                         }
1676
1677                         if( ( colon = strchr( line, ':' ) ) )
1678                         {
1679                             char *name;
1680                             char *value;
1681
1682                             *colon++ = '\0';
1683                             while( *colon == ' ' )
1684                             {
1685                                 colon++;
1686                             }
1687                             name = strdup( line );
1688                             value = strdup( colon );
1689
1690                             TAB_APPEND( cl->query.i_name, cl->query.name, name );
1691                             TAB_APPEND( cl->query.i_value,cl->query.value,value);
1692
1693                             if( !strcasecmp( name, "Content-Length" ) )
1694                             {
1695                                 cl->query.i_body = atol( value );
1696                             }
1697                         }
1698
1699                         if( p )
1700                         {
1701                             p++;
1702                             while( *p == '\n' || *p == '\r' )
1703                             {
1704                                 p++;
1705                             }
1706                         }
1707                     }
1708                 }
1709                 if( cl->query.i_body > 0 )
1710                 {
1711                     /* TODO Mhh, handle the case client will only send a request and close the connection
1712                      * to mark and of body (probably only RTSP) */
1713                     cl->query.p_body = malloc( cl->query.i_body );
1714                     cl->i_buffer = 0;
1715                 }
1716                 else
1717                 {
1718                     cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1719                 }
1720             }
1721         }
1722     }
1723
1724     /* check if the client is to be set to dead */
1725 #if defined( WIN32 ) || defined( UNDER_CE )
1726     if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1727 #else
1728     if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1729 #endif
1730     {
1731         if( cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE )
1732         {
1733             /* connection closed -> end of data */
1734             if( cl->query.i_body > 0 )
1735             {
1736                 cl->query.i_body = cl->i_buffer;
1737             }
1738             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1739         }
1740         else
1741         {
1742             cl->i_state = HTTPD_CLIENT_DEAD;
1743         }
1744     }
1745     cl->i_activity_date = mdate();
1746
1747     /* XXX: for QT I have to disable timeout. Try to find why */
1748     if( cl->query.i_proto == HTTPD_PROTO_RTSP )
1749         cl->i_activity_timeout = 0;
1750
1751 #if 0 /* Debugging only */
1752     if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1753     {
1754         int i;
1755
1756         fprintf( stderr, "received new request\n" );
1757         fprintf( stderr, "  - proto=%s\n",
1758                  cl->query.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP" );
1759         fprintf( stderr, "  - version=%d\n", cl->query.i_version );
1760         fprintf( stderr, "  - msg=%d\n", cl->query.i_type );
1761         if( cl->query.i_type == HTTPD_MSG_ANSWER )
1762         {
1763             fprintf( stderr, "  - answer=%d '%s'\n", cl->query.i_status,
1764                      cl->query.psz_status );
1765         }
1766         else if( cl->query.i_type != HTTPD_MSG_NONE )
1767         {
1768             fprintf( stderr, "  - url=%s\n", cl->query.psz_url );
1769         }
1770         for( i = 0; i < cl->query.i_name; i++ )
1771         {
1772             fprintf( stderr, "  - option name='%s' value='%s'\n",
1773                      cl->query.name[i], cl->query.value[i] );
1774         }
1775     }
1776 #endif
1777 }
1778
1779 static void httpd_ClientSend( httpd_client_t *cl )
1780 {
1781     int i;
1782     int i_len;
1783
1784     if( cl->i_buffer < 0 )
1785     {
1786         /* We need to create the header */
1787         int i_size = 0;
1788         char *p;
1789
1790         i_size = strlen( "HTTP/1.") + 10 + 10 +
1791                  strlen( cl->answer.psz_status ? cl->answer.psz_status : "" ) + 5;
1792         for( i = 0; i < cl->answer.i_name; i++ )
1793         {
1794             i_size += strlen( cl->answer.name[i] ) + 2 +
1795                       strlen( cl->answer.value[i] ) + 2;
1796         }
1797
1798         if( cl->i_buffer_size < i_size )
1799         {
1800             cl->i_buffer_size = i_size;
1801             free( cl->p_buffer );
1802             cl->p_buffer = malloc( i_size );
1803         }
1804         p = (char *)cl->p_buffer;
1805
1806         p += sprintf( p, "%s/1.%d %d %s\r\n",
1807                       cl->answer.i_proto ==  HTTPD_PROTO_HTTP ? "HTTP" : "RTSP",
1808                       cl->answer.i_version,
1809                       cl->answer.i_status, cl->answer.psz_status );
1810         for( i = 0; i < cl->answer.i_name; i++ )
1811         {
1812             p += sprintf( p, "%s: %s\r\n", cl->answer.name[i],
1813                           cl->answer.value[i] );
1814         }
1815         p += sprintf( p, "\r\n" );
1816
1817         cl->i_buffer = 0;
1818         cl->i_buffer_size = (uint8_t*)p - cl->p_buffer;
1819
1820         /*fprintf( stderr, "sending answer\n" );
1821         fprintf( stderr, "%s",  cl->p_buffer );*/
1822     }
1823
1824     i_len = httpd_NetSend( cl, &cl->p_buffer[cl->i_buffer],
1825                            cl->i_buffer_size - cl->i_buffer );
1826     if( i_len >= 0 )
1827     {
1828         cl->i_activity_date = mdate();
1829         cl->i_buffer += i_len;
1830
1831         if( cl->i_buffer >= cl->i_buffer_size )
1832         {
1833             if( cl->answer.i_body == 0  && cl->answer.i_body_offset > 0 &&
1834                 !cl->b_read_waiting )
1835             {
1836                 /* catch more body data */
1837                 int     i_msg = cl->query.i_type;
1838                 int64_t i_offset = cl->answer.i_body_offset;
1839
1840                 httpd_MsgClean( &cl->answer );
1841                 cl->answer.i_body_offset = i_offset;
1842
1843                 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
1844                                           &cl->answer, &cl->query );
1845             }
1846
1847             if( cl->answer.i_body > 0 )
1848             {
1849                 /* send the body data */
1850                 free( cl->p_buffer );
1851                 cl->p_buffer = cl->answer.p_body;
1852                 cl->i_buffer_size = cl->answer.i_body;
1853                 cl->i_buffer = 0;
1854
1855                 cl->answer.i_body = 0;
1856                 cl->answer.p_body = NULL;
1857             }
1858             else
1859             {
1860                 /* send finished */
1861                 cl->i_state = HTTPD_CLIENT_SEND_DONE;
1862             }
1863         }
1864     }
1865     else
1866     {
1867 #if defined( WIN32 ) || defined( UNDER_CE )
1868         if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1869 #else
1870         if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1871 #endif
1872         {
1873             /* error */
1874             cl->i_state = HTTPD_CLIENT_DEAD;
1875         }
1876     }
1877 }
1878
1879 static void httpd_ClientTlsHsIn( httpd_client_t *cl )
1880 {
1881     switch( tls_SessionContinueHandshake( cl->p_tls ) )
1882     {
1883         case 0:
1884             cl->i_state = HTTPD_CLIENT_RECEIVING;
1885             break;
1886
1887         case -1:
1888             cl->i_state = HTTPD_CLIENT_DEAD;
1889             cl->p_tls = NULL;
1890             break;
1891
1892         case 2:
1893             cl->i_state = HTTPD_CLIENT_TLS_HS_OUT;
1894     }
1895 }
1896
1897 static void httpd_ClientTlsHsOut( httpd_client_t *cl )
1898 {
1899     switch( tls_SessionContinueHandshake( cl->p_tls ) )
1900     {
1901         case 0:
1902             cl->i_state = HTTPD_CLIENT_RECEIVING;
1903             break;
1904
1905         case -1:
1906             cl->i_state = HTTPD_CLIENT_DEAD;
1907             cl->p_tls = NULL;
1908             break;
1909
1910         case 1:
1911             cl->i_state = HTTPD_CLIENT_TLS_HS_IN;
1912             break;
1913     }
1914 }
1915
1916 static void httpd_HostThread( httpd_host_t *host )
1917 {
1918     tls_session_t *p_tls = NULL;
1919
1920     stats_Create( host, "client_connections", STATS_CLIENT_CONNECTIONS,
1921                   VLC_VAR_INTEGER, STATS_COUNTER );
1922     stats_Create( host, "active_connections", STATS_ACTIVE_CONNECTIONS,
1923                   VLC_VAR_INTEGER, STATS_COUNTER );
1924
1925     while( !host->b_die )
1926     {
1927         struct timeval  timeout;
1928         fd_set          fds_read;
1929         fd_set          fds_write;
1930         /* FIXME: (too) many int variables */
1931         int             fd, i_fd;
1932         int             i_handle_max = 0;
1933         int             i_ret;
1934         int             i_client;
1935         int             b_low_delay = 0;
1936
1937         if( host->i_url <= 0 )
1938         {
1939             /* 0.2s */
1940             msleep( 200000 );
1941             continue;
1942         }
1943
1944         /* built a set of handle to select */
1945         FD_ZERO( &fds_read );
1946         FD_ZERO( &fds_write );
1947
1948         i_handle_max = -1;
1949
1950         for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ )
1951         {
1952             FD_SET( fd, &fds_read );
1953             if( fd > i_handle_max )
1954                 i_handle_max = fd;
1955         }
1956
1957         /* prepare a new TLS session */
1958         if( ( p_tls == NULL ) && ( host->p_tls != NULL ) )
1959             p_tls = tls_ServerSessionPrepare( host->p_tls );
1960
1961         /* add all socket that should be read/write and close dead connection */
1962         vlc_mutex_lock( &host->lock );
1963         for( i_client = 0; i_client < host->i_client; i_client++ )
1964         {
1965             httpd_client_t *cl = host->client[i_client];
1966
1967             if( cl->i_ref < 0 || ( cl->i_ref == 0 &&
1968                 ( cl->i_state == HTTPD_CLIENT_DEAD ||
1969                   ( cl->i_activity_timeout > 0 &&
1970                     cl->i_activity_date+cl->i_activity_timeout < mdate()) ) ) )
1971             {
1972                 httpd_ClientClean( cl );
1973                 stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, -1, NULL );
1974                 TAB_REMOVE( host->i_client, host->client, cl );
1975                 free( cl );
1976                 i_client--;
1977                 continue;
1978             }
1979             else if( ( cl->i_state == HTTPD_CLIENT_RECEIVING )
1980                   || ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) )
1981             {
1982                 FD_SET( cl->fd, &fds_read );
1983                 i_handle_max = __MAX( i_handle_max, cl->fd );
1984             }
1985             else if( ( cl->i_state == HTTPD_CLIENT_SENDING )
1986                   || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) )
1987             {
1988                 FD_SET( cl->fd, &fds_write );
1989                 i_handle_max = __MAX( i_handle_max, cl->fd );
1990             }
1991             else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1992             {
1993                 httpd_message_t *answer = &cl->answer;
1994                 httpd_message_t *query  = &cl->query;
1995                 int i_msg = query->i_type;
1996
1997                 httpd_MsgInit( answer );
1998
1999                 /* Handle what we received */
2000                 if( (cl->i_mode != HTTPD_CLIENT_BIDIR) &&
2001                     (i_msg == HTTPD_MSG_ANSWER || i_msg == HTTPD_MSG_CHANNEL) )
2002                 {
2003                     /* we can only receive request from client when not
2004                      * in BIDIR mode */
2005                     cl->url     = NULL;
2006                     cl->i_state = HTTPD_CLIENT_DEAD;
2007                 }
2008                 else if( i_msg == HTTPD_MSG_ANSWER )
2009                 {
2010                     /* We are in BIDIR mode, trigger the callback and then
2011                      * check for new data */
2012                     if( cl->url && cl->url->catch[i_msg].cb )
2013                     {
2014                         cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys,
2015                                                   cl, NULL, query );
2016                     }
2017                     cl->i_state = HTTPD_CLIENT_WAITING;
2018                 }
2019                 else if( i_msg == HTTPD_MSG_CHANNEL )
2020                 {
2021                     /* We are in BIDIR mode, trigger the callback and then
2022                      * check for new data */
2023                     if( cl->url && cl->url->catch[i_msg].cb )
2024                     {
2025                         cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys,
2026                                                   cl, NULL, query );
2027                     }
2028                     cl->i_state = HTTPD_CLIENT_WAITING;
2029                 }
2030                 else if( i_msg == HTTPD_MSG_OPTIONS )
2031                 {
2032                     int i_cseq;
2033
2034                     /* unimplemented */
2035                     answer->i_proto  = query->i_proto ;
2036                     answer->i_type   = HTTPD_MSG_ANSWER;
2037                     answer->i_version= 0;
2038                     answer->i_status = 200;
2039                     answer->psz_status = strdup( "Ok" );
2040
2041                     answer->i_body = 0;
2042                     answer->p_body = NULL;
2043
2044                     i_cseq = atoi( httpd_MsgGet( query, "Cseq" ) );
2045                     httpd_MsgAdd( answer, "Cseq", "%d", i_cseq );
2046                     httpd_MsgAdd( answer, "Server", "VLC Server" );
2047                     httpd_MsgAdd( answer, "Public", "DESCRIBE, SETUP, "
2048                                  "TEARDOWN, PLAY, PAUSE" );
2049                     httpd_MsgAdd( answer, "Content-Length", "%d",
2050                                   answer->i_body );
2051
2052                     cl->i_buffer = -1;  /* Force the creation of the answer in
2053                                          * httpd_ClientSend */
2054                     cl->i_state = HTTPD_CLIENT_SENDING;
2055                 }
2056                 else if( i_msg == HTTPD_MSG_NONE )
2057                 {
2058                     if( query->i_proto == HTTPD_PROTO_NONE )
2059                     {
2060                         cl->url = NULL;
2061                         cl->i_state = HTTPD_CLIENT_DEAD;
2062                     }
2063                     else
2064                     {
2065                         uint8_t *p;
2066
2067                         /* unimplemented */
2068                         answer->i_proto  = query->i_proto ;
2069                         answer->i_type   = HTTPD_MSG_ANSWER;
2070                         answer->i_version= 0;
2071                         answer->i_status = 501;
2072                         answer->psz_status = strdup( "Unimplemented" );
2073
2074                         p = answer->p_body = malloc( 1000 );
2075
2076                         p += sprintf( (char *)p,
2077                             "<?xml version=\"1.0\" encoding=\"ascii\" ?>"
2078                             "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2079                             "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2080                             "<html>\n"
2081                             "<head>\n"
2082                             "<title>Error 501</title>\n"
2083                             "</head>\n"
2084                             "<body>\n"
2085                             "<h1>501 Unimplemented</h1>\n"
2086                             "<hr />\n"
2087                             "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
2088                             "</body>\n"
2089                             "</html>\n" );
2090
2091                         answer->i_body = p - answer->p_body;
2092                         httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
2093
2094                         cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
2095                         cl->i_state = HTTPD_CLIENT_SENDING;
2096                     }
2097                 }
2098                 else
2099                 {
2100                     vlc_bool_t b_auth_failed = VLC_FALSE;
2101                     vlc_bool_t b_hosts_failed = VLC_FALSE;
2102                     int i;
2103
2104                     /* Search the url and trigger callbacks */
2105                     for( i = 0; i < host->i_url; i++ )
2106                     {
2107                         httpd_url_t *url = host->url[i];
2108
2109                         if( !strcmp( url->psz_url, query->psz_url ) )
2110                         {
2111                             if( url->catch[i_msg].cb )
2112                             {
2113                                 if( answer && ( url->p_acl != NULL ) )
2114                                 {
2115                                     char ip[NI_MAXNUMERICHOST];
2116
2117                                     if( httpd_ClientIP( cl, ip ) != NULL )
2118                                     {
2119                                         if( ACL_Check( url->p_acl, ip ) )
2120                                         {
2121                                             b_hosts_failed = VLC_TRUE;
2122                                             break;
2123                                         }
2124                                     }
2125                                     else
2126                                         b_hosts_failed = VLC_TRUE;
2127                                 }
2128
2129                                 if( answer && ( *url->psz_user || *url->psz_password ) )
2130                                 {
2131                                     /* create the headers */
2132                                     char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */
2133                                     char *auth;
2134                                     char *id;
2135
2136                                     asprintf( &id, "%s:%s", url->psz_user, url->psz_password );
2137                                     auth = malloc( strlen(b64) + 1 );
2138
2139                                     if( !strncasecmp( b64, "BASIC", 5 ) )
2140                                     {
2141                                         b64 += 5;
2142                                         while( *b64 == ' ' )
2143                                         {
2144                                             b64++;
2145                                         }
2146                                         b64_decode( auth, b64 );
2147                                     }
2148                                     else
2149                                     {
2150                                         strcpy( auth, "" );
2151                                     }
2152
2153                                     if( strcmp( id, auth ) )
2154                                     {
2155                                         httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user );
2156                                         /* We fail for all url */
2157                                         b_auth_failed = VLC_TRUE;
2158                                         free( id );
2159                                         free( auth );
2160                                         break;
2161                                     }
2162
2163                                     free( id );
2164                                     free( auth );
2165                                 }
2166
2167                                 if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) )
2168                                 {
2169                                     if( answer->i_proto == HTTPD_PROTO_NONE )
2170                                     {
2171                                         /* Raw answer from a CGI */
2172                                         cl->i_buffer = cl->i_buffer_size;
2173                                     }
2174                                     else
2175                                         cl->i_buffer = -1;
2176
2177                                     /* only one url can answer */
2178                                     answer = NULL;
2179                                     if( cl->url == NULL )
2180                                     {
2181                                         cl->url = url;
2182                                     }
2183                                 }
2184                             }
2185                         }
2186                     }
2187
2188                     if( answer )
2189                     {
2190                         uint8_t *p;
2191
2192                         answer->i_proto  = query->i_proto;
2193                         answer->i_type   = HTTPD_MSG_ANSWER;
2194                         answer->i_version= 0;
2195                         p = answer->p_body = malloc( 1000 + strlen(query->psz_url) );
2196
2197                         if( b_hosts_failed )
2198                         {
2199                             answer->i_status = 403;
2200                             answer->psz_status = strdup( "Forbidden" );
2201
2202                             /* FIXME: lots of code duplication */
2203                             p += sprintf( (char *)p,
2204                                 "<?xml version=\"1.0\" encoding=\"ascii\" ?>"
2205                                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2206                                 "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2207                                 "<html>\n"
2208                                 "<head>\n"
2209                                 "<title>Error 403</title>\n"
2210                                 "</head>\n"
2211                                 "<body>\n"
2212                                 "<h1>403 Forbidden (%s)</h1>\n"
2213                                 "<hr />\n"
2214                                 "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
2215                                 "</body>\n"
2216                                 "</html>\n", query->psz_url );
2217                         }
2218                         else if( b_auth_failed )
2219                         {
2220                             answer->i_status = 401;
2221                             answer->psz_status = strdup( "Authorization Required" );
2222
2223                             p += sprintf( (char *)p,
2224                                 "<?xml version=\"1.0\" encoding=\"ascii\" ?>"
2225                                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2226                                 "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2227                                 "<html>\n"
2228                                 "<head>\n"
2229                                 "<title>Error 401</title>\n"
2230                                 "</head>\n"
2231                                 "<body>\n"
2232                                 "<h1>401 Authorization Required (%s)</h1>\n"
2233                                 "<hr />\n"
2234                                 "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
2235                                 "</body>\n"
2236                                 "</html>\n", query->psz_url );
2237                         }
2238                         else
2239                         {
2240                             /* no url registered */
2241                             answer->i_status = 404;
2242                             answer->psz_status = strdup( "Not found" );
2243
2244                             p += sprintf( (char *)p,
2245                                 "<?xml version=\"1.0\" encoding=\"ascii\" ?>"
2246                                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2247                                 "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2248                                 "<html>\n"
2249                                 "<head>\n"
2250                                 "<title>Error 404</title>\n"
2251                                 "</head>\n"
2252                                 "<body>\n"
2253                                 "<h1>404 Resource not found(%s)</h1>\n"
2254                                 "<hr />\n"
2255                                 "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
2256                                 "</body>\n"
2257                                 "</html>\n", query->psz_url );
2258                         }
2259
2260                         answer->i_body = p - answer->p_body;
2261                         cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
2262                         httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
2263                     }
2264
2265                     cl->i_state = HTTPD_CLIENT_SENDING;
2266                 }
2267             }
2268             else if( cl->i_state == HTTPD_CLIENT_SEND_DONE )
2269             {
2270                 if( cl->i_mode == HTTPD_CLIENT_FILE || cl->answer.i_body_offset == 0 )
2271                 {
2272                     cl->url = NULL;
2273                     if( ( cl->query.i_proto == HTTPD_PROTO_HTTP &&
2274                           ( ( cl->answer.i_version == 0 && !strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Keep-Alive" ) ) ||
2275                             ( cl->answer.i_version == 1 &&  strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Close" ) ) ) ) ||
2276                         ( cl->query.i_proto == HTTPD_PROTO_RTSP &&
2277                           strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Close" ) &&
2278                           strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Close" ) ) )
2279                     {
2280                         httpd_MsgClean( &cl->query );
2281                         httpd_MsgInit( &cl->query );
2282
2283                         cl->i_buffer = 0;
2284                         cl->i_buffer_size = 1000;
2285                         free( cl->p_buffer );
2286                         cl->p_buffer = malloc( cl->i_buffer_size );
2287                         cl->i_state = HTTPD_CLIENT_RECEIVING;
2288                     }
2289                     else
2290                     {
2291                         cl->i_state = HTTPD_CLIENT_DEAD;
2292                     }
2293                     httpd_MsgClean( &cl->answer );
2294                 }
2295                 else if( cl->b_read_waiting )
2296                 {
2297                     /* we have a message waiting for us to read it */
2298                     httpd_MsgClean( &cl->answer );
2299                     httpd_MsgClean( &cl->query );
2300
2301                     cl->i_buffer = 0;
2302                     cl->i_buffer_size = 1000;
2303                     free( cl->p_buffer );
2304                     cl->p_buffer = malloc( cl->i_buffer_size );
2305                     cl->i_state = HTTPD_CLIENT_RECEIVING;
2306                     cl->b_read_waiting = VLC_FALSE;
2307                 }
2308                 else
2309                 {
2310                     int64_t i_offset = cl->answer.i_body_offset;
2311                     httpd_MsgClean( &cl->answer );
2312
2313                     cl->answer.i_body_offset = i_offset;
2314                     free( cl->p_buffer );
2315                     cl->p_buffer = NULL;
2316                     cl->i_buffer = 0;
2317                     cl->i_buffer_size = 0;
2318
2319                     cl->i_state = HTTPD_CLIENT_WAITING;
2320                 }
2321             }
2322             else if( cl->i_state == HTTPD_CLIENT_WAITING )
2323             {
2324                 int64_t i_offset = cl->answer.i_body_offset;
2325                 int     i_msg = cl->query.i_type;
2326
2327                 httpd_MsgInit( &cl->answer );
2328                 cl->answer.i_body_offset = i_offset;
2329
2330                 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
2331                                           &cl->answer, &cl->query );
2332                 if( cl->answer.i_type != HTTPD_MSG_NONE )
2333                 {
2334                     /* we have new data, so reenter send mode */
2335                     cl->i_buffer      = 0;
2336                     cl->p_buffer      = cl->answer.p_body;
2337                     cl->i_buffer_size = cl->answer.i_body;
2338                     cl->answer.p_body = NULL;
2339                     cl->answer.i_body = 0;
2340                     cl->i_state = HTTPD_CLIENT_SENDING;
2341                 }
2342                 else
2343                 {
2344                     /* we shouldn't wait too long */
2345                     b_low_delay = VLC_TRUE;
2346                 }
2347             }
2348
2349             /* Special for BIDIR mode we also check reading */
2350             if( cl->i_mode == HTTPD_CLIENT_BIDIR &&
2351                 cl->i_state == HTTPD_CLIENT_SENDING )
2352             {
2353                 FD_SET( cl->fd, &fds_read );
2354                 i_handle_max = __MAX( i_handle_max, cl->fd );
2355             }
2356         }
2357         vlc_mutex_unlock( &host->lock );
2358
2359         /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */
2360         timeout.tv_sec = 0;
2361         timeout.tv_usec = b_low_delay ? 20000 : 100000;
2362
2363         i_ret = select( i_handle_max + 1,
2364                         &fds_read, &fds_write, NULL, &timeout );
2365
2366         if( (i_ret == -1) && (errno != EINTR) )
2367         {
2368 #if defined(WIN32) || defined(UNDER_CE)
2369             msg_Warn( host, "cannot select sockets (%d)", WSAGetLastError( ) );
2370 #else
2371             msg_Warn( host, "cannot select sockets : %s", strerror( errno ) );
2372 #endif
2373             msleep( 1000 );
2374             continue;
2375         }
2376         else if( i_ret <= 0 )
2377         {
2378             continue;
2379         }
2380
2381         /* accept new connections */
2382         for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ )
2383         {
2384             if( FD_ISSET( fd, &fds_read ) )
2385             {
2386                 socklen_t i_sock_size = sizeof( struct sockaddr_storage );
2387                 struct  sockaddr_storage sock;
2388
2389                 fd = accept( fd, (struct sockaddr *)&sock, &i_sock_size );
2390                 msg_Info( host, "Accepting" );
2391                 if( fd >= 0 )
2392                 {
2393                     int i_state = 0;
2394
2395                     /* set this new socket non-block */
2396 #if defined( WIN32 ) || defined( UNDER_CE )
2397                     {
2398                         unsigned long i_dummy = 1;
2399                         ioctlsocket( fd, FIONBIO, &i_dummy );
2400                     }
2401 #else
2402                     fcntl( fd, F_SETFL, O_NONBLOCK );
2403 #endif
2404
2405                     if( p_tls != NULL)
2406                     {
2407                         switch ( tls_ServerSessionHandshake( p_tls, fd ) )
2408                         {
2409                             case -1:
2410                                 msg_Err( host, "Rejecting TLS connection" );
2411                                 net_Close( fd );
2412                                 fd = -1;
2413                                 p_tls = NULL;
2414                                 break;
2415
2416                             case 1: /* missing input - most likely */
2417                                 i_state = HTTPD_CLIENT_TLS_HS_IN;
2418                                 break;
2419
2420                             case 2: /* missing output */
2421                                 i_state = HTTPD_CLIENT_TLS_HS_OUT;
2422                                 break;
2423                         }
2424                     }
2425
2426                     if( fd >= 0 )
2427                     {
2428                         httpd_client_t *cl;
2429                         stats_UpdateInteger( host, STATS_CLIENT_CONNECTIONS,
2430                                              1, NULL );
2431                         stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, 1,
2432                                              NULL );
2433                         cl = httpd_ClientNew( fd, &sock, i_sock_size, p_tls );
2434                         p_tls = NULL;
2435                         vlc_mutex_lock( &host->lock );
2436                         TAB_APPEND( host->i_client, host->client, cl );
2437                         vlc_mutex_unlock( &host->lock );
2438
2439                         if( i_state != 0 )
2440                             cl->i_state = i_state; // override state for TLS
2441                     }
2442                 }
2443             }
2444         }
2445
2446         /* now try all others socket */
2447         vlc_mutex_lock( &host->lock );
2448         for( i_client = 0; i_client < host->i_client; i_client++ )
2449         {
2450             httpd_client_t *cl = host->client[i_client];
2451             if( cl->i_state == HTTPD_CLIENT_RECEIVING )
2452             {
2453                 httpd_ClientRecv( cl );
2454             }
2455             else if( cl->i_state == HTTPD_CLIENT_SENDING )
2456             {
2457                 httpd_ClientSend( cl );
2458             }
2459             else if( cl->i_state == HTTPD_CLIENT_TLS_HS_IN )
2460             {
2461                 httpd_ClientTlsHsIn( cl );
2462             }
2463             else if( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT )
2464             {
2465                 httpd_ClientTlsHsOut( cl );
2466             }
2467
2468             if( cl->i_mode == HTTPD_CLIENT_BIDIR &&
2469                 cl->i_state == HTTPD_CLIENT_SENDING &&
2470                 FD_ISSET( cl->fd, &fds_read ) )
2471             {
2472                 cl->b_read_waiting = VLC_TRUE;
2473             }
2474         }
2475         vlc_mutex_unlock( &host->lock );
2476     }
2477
2478     if( p_tls != NULL )
2479         tls_ServerSessionClose( p_tls );
2480 }
2481
2482 #else /* ENABLE_HTTPD */
2483
2484 /* We just define an empty wrapper */
2485 httpd_host_t *httpd_TLSHostNew( vlc_object_t *a, char *b, int c,
2486                                 tls_server_t *d )
2487 {
2488     msg_Err( a, "HTTP daemon support is disabled" );
2489     return NULL;
2490 }
2491
2492 httpd_host_t *httpd_HostNew( vlc_object_t *a, char *b, int c )
2493 {
2494     msg_Err( a, "HTTP daemon support is disabled" );
2495     return NULL;
2496 }
2497
2498 void httpd_HostDelete( httpd_host_t *a )
2499 {
2500 }
2501
2502 httpd_url_t *httpd_UrlNew( httpd_host_t *host, char *psz_url,
2503                            char *psz_user, char *psz_password,
2504                            const vlc_acl_t *p_acl )
2505 {
2506     return NULL;
2507 }
2508
2509 httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, char *psz_url,
2510                                  char *psz_user, char *psz_password,
2511                                  const vlc_acl_t *p_acl )
2512 {
2513     return NULL;
2514 }
2515
2516 int httpd_UrlCatch( httpd_url_t *a, int b, httpd_callback_t c,
2517                     httpd_callback_sys_t *d )
2518 {
2519     return 0;
2520 }
2521
2522 void httpd_UrlDelete( httpd_url_t *a )
2523 {
2524 }
2525
2526 char* httpd_ClientIP( httpd_client_t *cl, char *psz_ip )
2527 {
2528     return NULL;
2529 }
2530
2531 char* httpd_ServerIP( httpd_client_t *cl, char *psz_ip )
2532 {
2533     return NULL;
2534 }
2535
2536 void httpd_ClientModeStream( httpd_client_t *a )
2537 {
2538 }
2539
2540 void httpd_ClientModeBidir( httpd_client_t *a )
2541 {
2542 }
2543
2544 void httpd_FileDelete( httpd_file_t *a )
2545 {
2546 }
2547
2548 httpd_file_t *httpd_FileNew( httpd_host_t *a, char *b, char *c, char *d,
2549                              char *e, httpd_file_callback_t f,
2550                              httpd_file_sys_t *g )
2551 {
2552     return NULL;
2553 }
2554
2555 httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
2556                                    const char *psz_user,
2557                                    const char *psz_password,
2558                                    const vlc_acl_t *p_acl,
2559                                    httpd_handler_callback_t pf_fill,
2560                                    httpd_handler_sys_t *p_sys )
2561 {
2562     return NULL;
2563 }
2564
2565 void httpd_HandlerDelete( httpd_handler_t *handler )
2566 {
2567 }
2568
2569 void httpd_RedirectDelete( httpd_redirect_t *a )
2570 {
2571 }
2572
2573 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *a,
2574                                      char *b, char *c )
2575 {
2576     return NULL;
2577 }
2578
2579 void httpd_StreamDelete( httpd_stream_t *a )
2580 {
2581 }
2582
2583 int httpd_StreamHeader( httpd_stream_t *a, uint8_t *b, int c )
2584 {
2585     return 0;
2586 }
2587
2588 int httpd_StreamSend ( httpd_stream_t *a, uint8_t *b, int c )
2589 {
2590     return 0;
2591 }
2592
2593 httpd_stream_t *httpd_StreamNew( httpd_host_t *a, char *b, char *c,
2594                                  char *d, char *e )
2595 {
2596     return NULL;
2597 }
2598
2599 void httpd_MsgInit ( httpd_message_t *a )
2600 {
2601 }
2602
2603 void httpd_MsgAdd  ( httpd_message_t *a, char *b, char *c, ... )
2604 {
2605 }
2606
2607 char *httpd_MsgGet ( httpd_message_t *a, char *b )
2608 {
2609     return NULL;
2610 }
2611
2612 void httpd_MsgClean( httpd_message_t *a )
2613 {
2614 }
2615
2616 #endif /* ENABLE_HTTPD */