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