]> git.sesse.net Git - vlc/blob - src/network/httpd.c
Whenever using select() companion macro FD_SET(), one must ensure that
[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
626     if( answer == NULL || query == NULL )
627     {
628         return VLC_SUCCESS;
629     }
630     answer->i_proto  = query->i_proto;
631     answer->i_version= query->i_version;
632     answer->i_type   = HTTPD_MSG_ANSWER;
633     answer->i_status = 301;
634     answer->psz_status = strdup( "Moved Permanently" );
635
636     answer->i_body = asprintf( (char **)&answer->p_body,
637         "<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
638         "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
639         "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
640         "<html>\n"
641         "<head>\n"
642         "<title>Redirection</title>\n"
643         "</head>\n"
644         "<body>\n"
645         "<h1>You should be "
646         "<a href=\"%s\">redirected</a></h1>\n"
647         "<hr />\n"
648         "<p><a href=\"http://www.videolan.org\">VideoLAN</a>\n</p>"
649         "<hr />\n"
650         "</body>\n"
651         "</html>\n", rdir->psz_dst );
652
653     /* XXX check if it's ok or we need to set an absolute url */
654     httpd_MsgAdd( answer, "Location",  "%s", rdir->psz_dst );
655
656     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
657
658     return VLC_SUCCESS;
659 }
660
661 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst,
662                                      const char *psz_url_src )
663 {
664     httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) );
665
666     if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL, NULL ) ) )
667     {
668         free( rdir );
669         return NULL;
670     }
671     rdir->psz_dst = strdup( psz_url_dst );
672
673     /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
674     httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack,
675                     (httpd_callback_sys_t*)rdir );
676     httpd_UrlCatch( rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack,
677                     (httpd_callback_sys_t*)rdir );
678     httpd_UrlCatch( rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack,
679                     (httpd_callback_sys_t*)rdir );
680     httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack,
681                     (httpd_callback_sys_t*)rdir );
682
683     return rdir;
684 }
685 void httpd_RedirectDelete( httpd_redirect_t *rdir )
686 {
687     httpd_UrlDelete( rdir->url );
688     free( rdir->psz_dst );
689     free( rdir );
690 }
691
692 /*****************************************************************************
693  * High Level Funtions: httpd_stream_t
694  *****************************************************************************/
695 struct httpd_stream_t
696 {
697     vlc_mutex_t lock;
698     httpd_url_t *url;
699
700     char    *psz_mime;
701
702     /* Header to send as first packet */
703     uint8_t *p_header;
704     int     i_header;
705
706     /* circular buffer */
707     int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
708     uint8_t     *p_buffer;          /* buffer */
709     int64_t     i_buffer_pos;       /* absolute position from begining */
710     int64_t     i_buffer_last_pos;  /* a new connection will start with that */
711 };
712
713 static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
714                                  httpd_client_t *cl, httpd_message_t *answer,
715                                  httpd_message_t *query )
716 {
717     httpd_stream_t *stream = (httpd_stream_t*)p_sys;
718
719     if( answer == NULL || query == NULL || cl == NULL )
720     {
721         return VLC_SUCCESS;
722     }
723
724     if( answer->i_body_offset > 0 )
725     {
726         int64_t i_write;
727         int     i_pos;
728
729 #if 0
730         fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n",
731                  answer->i_body_offset );
732 #endif
733
734         if( answer->i_body_offset >= stream->i_buffer_pos )
735         {
736             /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */
737             return VLC_EGENERIC;    /* wait, no data available */
738         }
739         if( answer->i_body_offset + stream->i_buffer_size <
740             stream->i_buffer_pos )
741         {
742             /* this client isn't fast enough */
743 #if 0
744             fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n",
745                      answer->i_body_offset, stream->i_buffer_last_pos );
746 #endif
747             answer->i_body_offset = stream->i_buffer_last_pos;
748         }
749
750         i_pos   = answer->i_body_offset % stream->i_buffer_size;
751         i_write = stream->i_buffer_pos - answer->i_body_offset;
752         if( i_write > HTTPD_CL_BUFSIZE )
753         {
754             i_write = HTTPD_CL_BUFSIZE;
755         }
756         else if( i_write <= 0 )
757         {
758             return VLC_EGENERIC;    /* wait, no data available */
759         }
760
761         /* Don't go past the end of the circular buffer */
762         i_write = __MIN( i_write, stream->i_buffer_size - i_pos );
763
764         /* using HTTPD_MSG_ANSWER -> data available */
765         answer->i_proto  = HTTPD_PROTO_HTTP;
766         answer->i_version= 0;
767         answer->i_type   = HTTPD_MSG_ANSWER;
768
769         answer->i_body = i_write;
770         answer->p_body = malloc( i_write );
771         memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write );
772
773         answer->i_body_offset += i_write;
774
775         return VLC_SUCCESS;
776     }
777     else
778     {
779         answer->i_proto  = HTTPD_PROTO_HTTP;
780         answer->i_version= 0;
781         answer->i_type   = HTTPD_MSG_ANSWER;
782
783         answer->i_status = 200;
784         answer->psz_status = strdup( "OK" );
785
786         if( query->i_type != HTTPD_MSG_HEAD )
787         {
788             httpd_ClientModeStream( cl );
789             vlc_mutex_lock( &stream->lock );
790             /* Send the header */
791             if( stream->i_header > 0 )
792             {
793                 answer->i_body = stream->i_header;
794                 answer->p_body = malloc( stream->i_header );
795                 memcpy( answer->p_body, stream->p_header, stream->i_header );
796             }
797             answer->i_body_offset = stream->i_buffer_last_pos;
798             vlc_mutex_unlock( &stream->lock );
799         }
800         else
801         {
802             httpd_MsgAdd( answer, "Content-Length", "%d", 0 );
803             answer->i_body_offset = 0;
804         }
805
806         if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) )
807         {
808             vlc_bool_t b_xplaystream = VLC_FALSE;
809             int i;
810
811             httpd_MsgAdd( answer, "Content-type", "%s",
812                           "application/octet-stream" );
813             httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" );
814             httpd_MsgAdd( answer, "Pragma", "no-cache" );
815             httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff );
816             httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" );
817
818             /* Check if there is a xPlayStrm=1 */
819             for( i = 0; i < query->i_name; i++ )
820             {
821                 if( !strcasecmp( query->name[i],  "Pragma" ) &&
822                     strstr( query->value[i], "xPlayStrm=1" ) )
823                 {
824                     b_xplaystream = VLC_TRUE;
825                 }
826             }
827
828             if( !b_xplaystream )
829             {
830                 answer->i_body_offset = 0;
831             }
832         }
833         else
834         {
835             httpd_MsgAdd( answer, "Content-type",  "%s", stream->psz_mime );
836         }
837         httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
838         return VLC_SUCCESS;
839     }
840 }
841
842 httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
843                                  const char *psz_url, const char *psz_mime,
844                                  const char *psz_user, const char *psz_password,
845                                  const vlc_acl_t *p_acl )
846 {
847     httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) );
848
849     if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user,
850                                             psz_password, p_acl )
851         ) == NULL )
852     {
853         free( stream );
854         return NULL;
855     }
856     vlc_mutex_init( host, &stream->lock );
857     if( psz_mime && *psz_mime )
858     {
859         stream->psz_mime = strdup( psz_mime );
860     }
861     else
862     {
863         stream->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
864     }
865     stream->i_header = 0;
866     stream->p_header = NULL;
867     stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
868     stream->p_buffer = malloc( stream->i_buffer_size );
869     /* We set to 1 to make life simpler
870      * (this way i_body_offset can never be 0) */
871     stream->i_buffer_pos = 1;
872     stream->i_buffer_last_pos = 1;
873
874     httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack,
875                     (httpd_callback_sys_t*)stream );
876     httpd_UrlCatch( stream->url, HTTPD_MSG_GET, httpd_StreamCallBack,
877                     (httpd_callback_sys_t*)stream );
878     httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack,
879                     (httpd_callback_sys_t*)stream );
880
881     return stream;
882 }
883
884 int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data )
885 {
886     vlc_mutex_lock( &stream->lock );
887     if( stream->p_header )
888     {
889         free( stream->p_header );
890         stream->p_header = NULL;
891     }
892     stream->i_header = i_data;
893     if( i_data > 0 )
894     {
895         stream->p_header = malloc( i_data );
896         memcpy( stream->p_header, p_data, i_data );
897     }
898     vlc_mutex_unlock( &stream->lock );
899
900     return VLC_SUCCESS;
901 }
902
903 int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data )
904 {
905     int i_count;
906     int i_pos;
907
908     if( i_data < 0 || p_data == NULL )
909     {
910         return VLC_SUCCESS;
911     }
912     vlc_mutex_lock( &stream->lock );
913
914     /* save this pointer (to be used by new connection) */
915     stream->i_buffer_last_pos = stream->i_buffer_pos;
916
917     i_pos = stream->i_buffer_pos % stream->i_buffer_size;
918     i_count = i_data;
919     while( i_count > 0)
920     {
921         int i_copy;
922
923         i_copy = __MIN( i_count, stream->i_buffer_size - i_pos );
924
925         /* Ok, we can't go past the end of our buffer */
926         memcpy( &stream->p_buffer[i_pos], p_data, i_copy );
927
928         i_pos = ( i_pos + i_copy ) % stream->i_buffer_size;
929         i_count -= i_copy;
930         p_data  += i_copy;
931     }
932
933     stream->i_buffer_pos += i_data;
934
935     vlc_mutex_unlock( &stream->lock );
936     return VLC_SUCCESS;
937 }
938
939 void httpd_StreamDelete( httpd_stream_t *stream )
940 {
941     httpd_UrlDelete( stream->url );
942     vlc_mutex_destroy( &stream->lock );
943     if( stream->psz_mime ) free( stream->psz_mime );
944     if( stream->p_header ) free( stream->p_header );
945     if( stream->p_buffer ) free( stream->p_buffer );
946     free( stream );
947 }
948
949 /*****************************************************************************
950  * Low level
951  *****************************************************************************/
952 static void httpd_HostThread( httpd_host_t * );
953
954 /* create a new host */
955 httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host,
956                              int i_port )
957 {
958     return httpd_TLSHostNew( p_this, psz_host, i_port, NULL, NULL, NULL, NULL
959                            );
960 }
961
962 httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
963                                 int i_port,
964                                 const char *psz_cert, const char *psz_key,
965                                 const char *psz_ca, const char *psz_crl )
966 {
967     httpd_t      *httpd;
968     httpd_host_t *host;
969     tls_server_t *p_tls;
970     char *psz_host;
971     vlc_value_t  lockval;
972     int i;
973
974     if( psz_hostname == NULL )
975         psz_hostname = "";
976
977     psz_host = strdup( psz_hostname );
978     if( psz_host == NULL )
979     {
980         msg_Err( p_this, "memory error" );
981         return NULL;
982     }
983
984     /* to be sure to avoid multiple creation */
985     var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX );
986     var_Get( p_this->p_libvlc, "httpd_mutex", &lockval );
987     vlc_mutex_lock( lockval.p_address );
988
989     if( !(httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE )) )
990     {
991         msg_Info( p_this, "creating httpd" );
992         if( ( httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ) ) == NULL )
993         {
994             vlc_mutex_unlock( lockval.p_address );
995             free( psz_host );
996             return NULL;
997         }
998
999         httpd->i_host = 0;
1000         httpd->host   = NULL;
1001
1002         vlc_object_yield( httpd );
1003         vlc_object_attach( httpd, p_this->p_vlc );
1004     }
1005
1006     /* verify if it already exist */
1007     for( i = httpd->i_host - 1; i >= 0; i-- )
1008     {
1009         host = httpd->host[i];
1010
1011         /* cannot mix TLS and non-TLS hosts */
1012         if( ( ( httpd->host[i]->p_tls != NULL ) != ( psz_cert != NULL ) )
1013          || ( host->i_port != i_port )
1014          || strcmp( host->psz_hostname, psz_hostname ) )
1015             continue;
1016
1017         /* yep found */
1018         host->i_ref++;
1019
1020         vlc_mutex_unlock( lockval.p_address );
1021         return host;
1022     }
1023
1024     host = NULL;
1025
1026     /* determine TLS configuration */
1027     if ( psz_cert != NULL )
1028     {
1029         p_tls = tls_ServerCreate( p_this, psz_cert, psz_key );
1030         if ( p_tls == NULL )
1031         {
1032             msg_Err( p_this, "TLS initialization error" );
1033             goto error;
1034         }
1035
1036         if ( ( psz_ca != NULL) && tls_ServerAddCA( p_tls, psz_ca ) )
1037         {
1038             msg_Err( p_this, "TLS CA error" );
1039             goto error;
1040         }
1041
1042         if ( ( psz_crl != NULL) && tls_ServerAddCRL( p_tls, psz_crl ) )
1043         {
1044             msg_Err( p_this, "TLS CRL error" );
1045             goto error;
1046         }
1047     }
1048     else
1049         p_tls = NULL;
1050
1051     /* create the new host */
1052     host = vlc_object_create( p_this, VLC_OBJECT_HTTPD_HOST );
1053     host->httpd = httpd;
1054     vlc_mutex_init( httpd, &host->lock );
1055     host->i_ref = 1;
1056
1057     host->fd = net_ListenTCP( p_this, psz_host, i_port );
1058     if( host->fd == NULL )
1059     {
1060         msg_Err( p_this, "cannot create socket(s) for HTTP host" );
1061         goto error;
1062     }
1063
1064     host->i_port = i_port;
1065     host->psz_hostname = psz_host;
1066
1067     host->i_url     = 0;
1068     host->url       = NULL;
1069     host->i_client  = 0;
1070     host->client    = NULL;
1071
1072     host->p_tls = p_tls;
1073
1074     /* create the thread */
1075     if( vlc_thread_create( host, "httpd host thread", httpd_HostThread,
1076                            VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
1077     {
1078         msg_Err( p_this, "cannot spawn http host thread" );
1079         goto error;
1080     }
1081
1082     /* now add it to httpd */
1083     TAB_APPEND( httpd->i_host, httpd->host, host );
1084     vlc_mutex_unlock( lockval.p_address );
1085
1086     return host;
1087
1088 error:
1089     free( psz_host );
1090     if( httpd->i_host <= 0 )
1091     {
1092         vlc_object_release( httpd );
1093         vlc_object_detach( httpd );
1094         vlc_object_destroy( httpd );
1095     }
1096     vlc_mutex_unlock( lockval.p_address );
1097
1098     if( host != NULL )
1099     {
1100         net_ListenClose( host->fd );
1101         vlc_mutex_destroy( &host->lock );
1102         vlc_object_destroy( host );
1103     }
1104
1105     if( p_tls != NULL )
1106         tls_ServerDelete( p_tls );
1107
1108     return NULL;
1109 }
1110
1111 /* delete a host */
1112 void httpd_HostDelete( httpd_host_t *host )
1113 {
1114     httpd_t *httpd = host->httpd;
1115     vlc_value_t lockval;
1116     int i;
1117
1118     var_Get( httpd->p_libvlc, "httpd_mutex", &lockval );
1119     vlc_mutex_lock( lockval.p_address );
1120
1121     host->i_ref--;
1122     if( host->i_ref > 0 )
1123     {
1124         /* still used */
1125         vlc_mutex_unlock( lockval.p_address );
1126         msg_Dbg( host, "httpd_HostDelete: host still used" );
1127         return;
1128     }
1129     TAB_REMOVE( httpd->i_host, httpd->host, host );
1130
1131     host->b_die = 1;
1132     vlc_thread_join( host );
1133
1134     msg_Dbg( host, "HTTP host removed" );
1135
1136     for( i = 0; i < host->i_url; i++ )
1137     {
1138         msg_Err( host, "url still registered: %s", host->url[i]->psz_url );
1139     }
1140     for( i = 0; i < host->i_client; i++ )
1141     {
1142         httpd_client_t *cl = host->client[i];
1143         msg_Warn( host, "client still connected" );
1144         httpd_ClientClean( cl );
1145         TAB_REMOVE( host->i_client, host->client, cl );
1146         free( cl );
1147         i--;
1148         /* TODO */
1149     }
1150
1151     if( host->p_tls != NULL)
1152         tls_ServerDelete( host->p_tls );
1153
1154     net_ListenClose( host->fd );
1155     free( host->psz_hostname );
1156
1157     vlc_mutex_destroy( &host->lock );
1158     vlc_object_destroy( host );
1159
1160     vlc_object_release( httpd );
1161     if( httpd->i_host <= 0 )
1162     {
1163         msg_Dbg( httpd, "no host left, stopping httpd" );
1164         vlc_object_detach( httpd );
1165         vlc_object_destroy( httpd );
1166     }
1167     vlc_mutex_unlock( lockval.p_address );
1168 }
1169
1170 /* register a new url */
1171 static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url,
1172                                          const char *psz_user, const char *psz_password,
1173                                          const vlc_acl_t *p_acl, vlc_bool_t b_check )
1174 {
1175     httpd_url_t *url;
1176     int         i;
1177
1178     vlc_mutex_lock( &host->lock );
1179     if( b_check )
1180     {
1181         for( i = 0; i < host->i_url; i++ )
1182         {
1183             if( !strcmp( psz_url, host->url[i]->psz_url ) )
1184             {
1185                 msg_Warn( host->httpd,
1186                           "cannot add '%s' (url already defined)", psz_url );
1187                 vlc_mutex_unlock( &host->lock );
1188                 return NULL;
1189             }
1190         }
1191     }
1192
1193     url = malloc( sizeof( httpd_url_t ) );
1194     url->host = host;
1195
1196     vlc_mutex_init( host->httpd, &url->lock );
1197     url->psz_url = strdup( psz_url );
1198     url->psz_user = strdup( psz_user ? psz_user : "" );
1199     url->psz_password = strdup( psz_password ? psz_password : "" );
1200     url->p_acl = ACL_Duplicate( host, p_acl );
1201     for( i = 0; i < HTTPD_MSG_MAX; i++ )
1202     {
1203         url->catch[i].cb = NULL;
1204         url->catch[i].p_sys = NULL;
1205     }
1206
1207     TAB_APPEND( host->i_url, host->url, url );
1208     vlc_mutex_unlock( &host->lock );
1209
1210     return url;
1211 }
1212
1213 httpd_url_t *httpd_UrlNew( httpd_host_t *host, const char *psz_url,
1214                            const char *psz_user, const char *psz_password,
1215                            const vlc_acl_t *p_acl )
1216 {
1217     return httpd_UrlNewPrivate( host, psz_url, psz_user,
1218                                 psz_password, p_acl, VLC_FALSE );
1219 }
1220
1221 httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, const char *psz_url,
1222                                  const char *psz_user, const char *psz_password,
1223                                  const vlc_acl_t *p_acl )
1224 {
1225     return httpd_UrlNewPrivate( host, psz_url, psz_user,
1226                                 psz_password, p_acl, VLC_TRUE );
1227 }
1228
1229 /* register callback on a url */
1230 int httpd_UrlCatch( httpd_url_t *url, int i_msg, httpd_callback_t cb,
1231                     httpd_callback_sys_t *p_sys )
1232 {
1233     vlc_mutex_lock( &url->lock );
1234     url->catch[i_msg].cb   = cb;
1235     url->catch[i_msg].p_sys= p_sys;
1236     vlc_mutex_unlock( &url->lock );
1237
1238     return VLC_SUCCESS;
1239 }
1240
1241 /* delete an url */
1242 void httpd_UrlDelete( httpd_url_t *url )
1243 {
1244     httpd_host_t *host = url->host;
1245     int          i;
1246
1247     vlc_mutex_lock( &host->lock );
1248     TAB_REMOVE( host->i_url, host->url, url );
1249
1250     vlc_mutex_destroy( &url->lock );
1251     free( url->psz_url );
1252     free( url->psz_user );
1253     free( url->psz_password );
1254     ACL_Destroy( url->p_acl );
1255
1256     for( i = 0; i < host->i_client; i++ )
1257     {
1258         httpd_client_t *client = host->client[i];
1259
1260         if( client->url == url )
1261         {
1262             /* TODO complete it */
1263             msg_Warn( host, "force closing connections" );
1264             httpd_ClientClean( client );
1265             TAB_REMOVE( host->i_client, host->client, client );
1266             free( client );
1267             i--;
1268         }
1269     }
1270     free( url );
1271     vlc_mutex_unlock( &host->lock );
1272 }
1273
1274 void httpd_MsgInit( httpd_message_t *msg )
1275 {
1276     msg->cl         = NULL;
1277     msg->i_type     = HTTPD_MSG_NONE;
1278     msg->i_proto    = HTTPD_PROTO_NONE;
1279     msg->i_version  = -1;
1280
1281     msg->i_status   = 0;
1282     msg->psz_status = NULL;
1283
1284     msg->psz_url    = NULL;
1285     msg->psz_args   = NULL;
1286
1287     msg->i_channel  = -1;
1288
1289     msg->i_name     = 0;
1290     msg->name       = NULL;
1291     msg->i_value    = 0;
1292     msg->value      = NULL;
1293
1294     msg->i_body_offset = 0;
1295     msg->i_body        = 0;
1296     msg->p_body        = 0;
1297 }
1298
1299 void httpd_MsgClean( httpd_message_t *msg )
1300 {
1301     int i;
1302
1303     if( msg->psz_status )
1304     {
1305         free( msg->psz_status );
1306     }
1307     if( msg->psz_url )
1308     {
1309         free( msg->psz_url );
1310     }
1311     if( msg->psz_args )
1312     {
1313         free( msg->psz_args );
1314     }
1315     for( i = 0; i < msg->i_name; i++ )
1316     {
1317         free( msg->name[i] );
1318         free( msg->value[i] );
1319     }
1320     if( msg->name )
1321     {
1322         free( msg->name );
1323     }
1324     if( msg->value )
1325     {
1326         free( msg->value );
1327     }
1328     if( msg->p_body )
1329     {
1330         free( msg->p_body );
1331     }
1332     httpd_MsgInit( msg );
1333 }
1334
1335 char *httpd_MsgGet( httpd_message_t *msg, char *name )
1336 {
1337     int i;
1338
1339     for( i = 0; i < msg->i_name; i++ )
1340     {
1341         if( !strcasecmp( msg->name[i], name ))
1342         {
1343             return msg->value[i];
1344         }
1345     }
1346     return NULL;
1347 }
1348
1349 void httpd_MsgAdd( httpd_message_t *msg, char *name, char *psz_value, ... )
1350 {
1351     va_list args;
1352     char *value = NULL;
1353
1354     va_start( args, psz_value );
1355 #if defined(HAVE_VASPRINTF) && !defined(__APPLE__) && !defined(SYS_BEOS)
1356     vasprintf( &value, psz_value, args );
1357 #else
1358     {
1359         int i_size = strlen( psz_value ) + 4096;    /* FIXME stupid system */
1360         value = calloc( i_size, sizeof( char ) );
1361         vsnprintf( value, i_size, psz_value, args );
1362         value[i_size - 1] = 0;
1363     }
1364 #endif
1365     va_end( args );
1366
1367     name = strdup( name );
1368
1369     TAB_APPEND( msg->i_name,  msg->name,  name );
1370     TAB_APPEND( msg->i_value, msg->value, value );
1371 }
1372
1373 static void httpd_ClientInit( httpd_client_t *cl )
1374 {
1375     cl->i_state = HTTPD_CLIENT_RECEIVING;
1376     cl->i_activity_date = mdate();
1377     cl->i_activity_timeout = I64C(10000000);
1378     cl->i_buffer_size = HTTPD_CL_BUFSIZE;
1379     cl->i_buffer = 0;
1380     cl->p_buffer = malloc( cl->i_buffer_size );
1381     cl->i_mode   = HTTPD_CLIENT_FILE;
1382     cl->b_read_waiting = VLC_FALSE;
1383
1384     httpd_MsgInit( &cl->query );
1385     httpd_MsgInit( &cl->answer );
1386 }
1387
1388 void httpd_ClientModeStream( httpd_client_t *cl )
1389 {
1390     cl->i_mode   = HTTPD_CLIENT_STREAM;
1391 }
1392
1393 void httpd_ClientModeBidir( httpd_client_t *cl )
1394 {
1395     cl->i_mode   = HTTPD_CLIENT_BIDIR;
1396 }
1397
1398 char* httpd_ClientIP( const httpd_client_t *cl, char *psz_ip )
1399 {
1400     return net_GetPeerAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip;
1401 }
1402
1403 char* httpd_ServerIP( const httpd_client_t *cl, char *psz_ip )
1404 {
1405     return net_GetSockAddress( cl->fd, psz_ip, NULL ) ? NULL : psz_ip;
1406 }
1407
1408 static void httpd_ClientClean( httpd_client_t *cl )
1409 {
1410     if( cl->fd >= 0 )
1411     {
1412         if( cl->p_tls != NULL )
1413             tls_ServerSessionClose( cl->p_tls );
1414         net_Close( cl->fd );
1415         cl->fd = -1;
1416     }
1417
1418     httpd_MsgClean( &cl->answer );
1419     httpd_MsgClean( &cl->query );
1420
1421     if( cl->p_buffer )
1422     {
1423         free( cl->p_buffer );
1424         cl->p_buffer = NULL;
1425     }
1426 }
1427
1428 static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_storage *sock,
1429                                         int i_sock_size,
1430                                         tls_session_t *p_tls )
1431 {
1432     httpd_client_t *cl = malloc( sizeof( httpd_client_t ) );
1433
1434     if( !cl ) return NULL;
1435
1436     cl->i_ref   = 0;
1437     cl->fd      = fd;
1438     memcpy( &cl->sock, sock, sizeof( cl->sock ) );
1439     cl->i_sock_size = i_sock_size;
1440     cl->url     = NULL;
1441     cl->p_tls = p_tls;
1442
1443     httpd_ClientInit( cl );
1444
1445     return cl;
1446 }
1447
1448 static int httpd_NetRecv( httpd_client_t *cl, uint8_t *p, int i_len )
1449 {
1450     tls_session_t *p_tls;
1451
1452     p_tls = cl->p_tls;
1453     if( p_tls != NULL)
1454         return tls_Recv( p_tls, p, i_len );
1455
1456     return recv( cl->fd, p, i_len, 0 );
1457 }
1458
1459 static int httpd_NetSend( httpd_client_t *cl, const uint8_t *p, int i_len )
1460 {
1461     tls_session_t *p_tls;
1462
1463     p_tls = cl->p_tls;
1464     if( p_tls != NULL)
1465         return tls_Send( p_tls, p, i_len );
1466
1467     return send( cl->fd, p, i_len, 0 );
1468 }
1469
1470 static void httpd_ClientRecv( httpd_client_t *cl )
1471 {
1472     int i_len;
1473
1474     if( cl->query.i_proto == HTTPD_PROTO_NONE )
1475     {
1476         /* enough to see if it's rtp over rtsp or RTSP/HTTP */
1477         i_len = httpd_NetRecv( cl, &cl->p_buffer[cl->i_buffer],
1478                                4 - cl->i_buffer );
1479         if( i_len > 0 )
1480         {
1481             cl->i_buffer += i_len;
1482         }
1483
1484         if( cl->i_buffer >= 4 )
1485         {
1486             /*fprintf( stderr, "peek=%4.4s\n", cl->p_buffer );*/
1487             /* detect type */
1488             if( cl->p_buffer[0] == '$' )
1489             {
1490                 /* RTSP (rtp over rtsp) */
1491                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1492                 cl->query.i_type  = HTTPD_MSG_CHANNEL;
1493                 cl->query.i_channel = cl->p_buffer[1];
1494                 cl->query.i_body  = (cl->p_buffer[2] << 8)|cl->p_buffer[3];
1495                 cl->query.p_body  = malloc( cl->query.i_body );
1496
1497                 cl->i_buffer      = 0;
1498             }
1499             else if( !memcmp( cl->p_buffer, "HTTP", 4 ) )
1500             {
1501                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1502                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1503             }
1504             else if( !memcmp( cl->p_buffer, "RTSP", 4 ) )
1505             {
1506                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1507                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1508             }
1509             else if( !memcmp( cl->p_buffer, "GET ", 4 ) ||
1510                      !memcmp( cl->p_buffer, "HEAD", 4 ) ||
1511                      !memcmp( cl->p_buffer, "POST", 4 ) )
1512             {
1513                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1514                 cl->query.i_type  = HTTPD_MSG_NONE;
1515             }
1516             else
1517             {
1518                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1519                 cl->query.i_type  = HTTPD_MSG_NONE;
1520             }
1521         }
1522     }
1523     else if( cl->query.i_body > 0 )
1524     {
1525         /* we are reading the body of a request or a channel */
1526         i_len = httpd_NetRecv( cl, &cl->query.p_body[cl->i_buffer],
1527                                cl->query.i_body - cl->i_buffer );
1528         if( i_len > 0 )
1529         {
1530             cl->i_buffer += i_len;
1531         }
1532         if( cl->i_buffer >= cl->query.i_body )
1533         {
1534             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1535         }
1536     }
1537     else
1538     {
1539         /* we are reading a header -> char by char */
1540         for( ;; )
1541         {
1542             i_len = httpd_NetRecv (cl, &cl->p_buffer[cl->i_buffer], 1 );
1543             if( i_len <= 0 )
1544             {
1545                 break;
1546             }
1547             cl->i_buffer++;
1548
1549             if( cl->i_buffer + 1 >= cl->i_buffer_size )
1550             {
1551                 cl->i_buffer_size += 1024;
1552                 cl->p_buffer = realloc( cl->p_buffer, cl->i_buffer_size );
1553             }
1554             if( ( cl->i_buffer >= 2 && !memcmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )||
1555                 ( cl->i_buffer >= 4 && !memcmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) )
1556             {
1557                 char *p;
1558
1559                 /* we have finished the header so parse it and set i_body */
1560                 cl->p_buffer[cl->i_buffer] = '\0';
1561
1562                 if( cl->query.i_type == HTTPD_MSG_ANSWER )
1563                 {
1564                     /* FIXME:
1565                      * assume strlen( "HTTP/1.x" ) = 8
1566                      */
1567                     cl->query.i_status =
1568                         strtol( (char *)&cl->p_buffer[8],
1569                                 &p, 0 );
1570                     while( *p == ' ' )
1571                     {
1572                         p++;
1573                     }
1574                     cl->query.psz_status = strdup( p );
1575                 }
1576                 else
1577                 {
1578                     static const struct
1579                     {
1580                         char *name;
1581                         int  i_type;
1582                         int  i_proto;
1583                     }
1584                     msg_type[] =
1585                     {
1586                         { "OPTIONS",        HTTPD_MSG_OPTIONS,      HTTPD_PROTO_RTSP },
1587                         { "DESCRIBE",       HTTPD_MSG_DESCRIBE,     HTTPD_PROTO_RTSP },
1588                         { "SETUP",          HTTPD_MSG_SETUP,        HTTPD_PROTO_RTSP },
1589                         { "PLAY",           HTTPD_MSG_PLAY,         HTTPD_PROTO_RTSP },
1590                         { "PAUSE",          HTTPD_MSG_PAUSE,        HTTPD_PROTO_RTSP },
1591                         { "GET_PARAMETER",  HTTPD_MSG_GETPARAMETER, HTTPD_PROTO_RTSP },
1592                         { "TEARDOWN",       HTTPD_MSG_TEARDOWN,     HTTPD_PROTO_RTSP },
1593
1594                         { "GET",            HTTPD_MSG_GET,          HTTPD_PROTO_HTTP },
1595                         { "HEAD",           HTTPD_MSG_HEAD,         HTTPD_PROTO_HTTP },
1596                         { "POST",           HTTPD_MSG_POST,         HTTPD_PROTO_HTTP },
1597
1598                         { NULL,             HTTPD_MSG_NONE,         HTTPD_PROTO_NONE }
1599                     };
1600                     int  i;
1601
1602                     p = NULL;
1603                     cl->query.i_type = HTTPD_MSG_NONE;
1604
1605                     /*fprintf( stderr, "received new request=%s\n", cl->p_buffer);*/
1606
1607                     for( i = 0; msg_type[i].name != NULL; i++ )
1608                     {
1609                         if( !strncmp( (char *)cl->p_buffer, msg_type[i].name,
1610                                       strlen( msg_type[i].name ) ) )
1611                         {
1612                             p = (char *)&cl->p_buffer[strlen((char *)msg_type[i].name) + 1 ];
1613                             cl->query.i_type = msg_type[i].i_type;
1614                             if( cl->query.i_proto != msg_type[i].i_proto )
1615                             {
1616                                 p = NULL;
1617                                 cl->query.i_proto = HTTPD_PROTO_NONE;
1618                                 cl->query.i_type = HTTPD_MSG_NONE;
1619                             }
1620                             break;
1621                         }
1622                     }
1623                     if( p == NULL )
1624                     {
1625                         if( strstr( (char *)cl->p_buffer, "HTTP/1." ) )
1626                         {
1627                             cl->query.i_proto = HTTPD_PROTO_HTTP;
1628                         }
1629                         else if( strstr( (char *)cl->p_buffer, "RTSP/1." ) )
1630                         {
1631                             cl->query.i_proto = HTTPD_PROTO_RTSP;
1632                         }
1633                     }
1634                     else
1635                     {
1636                         char *p2;
1637                         char *p3;
1638
1639                         while( *p == ' ' )
1640                         {
1641                             p++;
1642                         }
1643                         p2 = strchr( p, ' ' );
1644                         if( p2 )
1645                         {
1646                             *p2++ = '\0';
1647                         }
1648                         if( !strncasecmp( p, "rtsp:", 5 ) )
1649                         {
1650                             /* for rtsp url, you have rtsp://localhost:port/path */
1651                             p += 5;
1652                             while( *p == '/' ) p++;
1653                             while( *p && *p != '/' ) p++;
1654                         }
1655                         cl->query.psz_url = strdup( p );
1656                         if( ( p3 = strchr( cl->query.psz_url, '?' ) )  )
1657                         {
1658                             *p3++ = '\0';
1659                             cl->query.psz_args = (uint8_t *)strdup( p3 );
1660                         }
1661                         if( p2 )
1662                         {
1663                             while( *p2 == ' ' )
1664                             {
1665                                 p2++;
1666                             }
1667                             if( !strncasecmp( p2, "HTTP/1.", 7 ) )
1668                             {
1669                                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1670                                 cl->query.i_version = atoi( p2+7 );
1671                             }
1672                             else if( !strncasecmp( p2, "RTSP/1.", 7 ) )
1673                             {
1674                                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1675                                 cl->query.i_version = atoi( p2+7 );
1676                             }
1677                         }
1678                         p = p2;
1679                     }
1680                 }
1681                 if( p )
1682                 {
1683                     p = strchr( p, '\n' );
1684                 }
1685                 if( p )
1686                 {
1687                     while( *p == '\n' || *p == '\r' )
1688                     {
1689                         p++;
1690                     }
1691                     while( p && *p != '\0' )
1692                     {
1693                         char *line = p;
1694                         char *eol = p = strchr( p, '\n' );
1695                         char *colon;
1696
1697                         while( eol && eol >= line && ( *eol == '\n' || *eol == '\r' ) )
1698                         {
1699                             *eol-- = '\0';
1700                         }
1701
1702                         if( ( colon = strchr( line, ':' ) ) )
1703                         {
1704                             char *name;
1705                             char *value;
1706
1707                             *colon++ = '\0';
1708                             while( *colon == ' ' )
1709                             {
1710                                 colon++;
1711                             }
1712                             name = strdup( line );
1713                             value = strdup( colon );
1714
1715                             TAB_APPEND( cl->query.i_name, cl->query.name, name );
1716                             TAB_APPEND( cl->query.i_value,cl->query.value,value);
1717
1718                             if( !strcasecmp( name, "Content-Length" ) )
1719                             {
1720                                 cl->query.i_body = atol( value );
1721                             }
1722                         }
1723
1724                         if( p )
1725                         {
1726                             p++;
1727                             while( *p == '\n' || *p == '\r' )
1728                             {
1729                                 p++;
1730                             }
1731                         }
1732                     }
1733                 }
1734                 if( cl->query.i_body > 0 )
1735                 {
1736                     /* TODO Mhh, handle the case client will only send a request and close the connection
1737                      * to mark and of body (probably only RTSP) */
1738                     cl->query.p_body = malloc( cl->query.i_body );
1739                     cl->i_buffer = 0;
1740                 }
1741                 else
1742                 {
1743                     cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1744                 }
1745             }
1746         }
1747     }
1748
1749     /* check if the client is to be set to dead */
1750 #if defined( WIN32 ) || defined( UNDER_CE )
1751     if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1752 #else
1753     if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1754 #endif
1755     {
1756         if( cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE )
1757         {
1758             /* connection closed -> end of data */
1759             if( cl->query.i_body > 0 )
1760             {
1761                 cl->query.i_body = cl->i_buffer;
1762             }
1763             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1764         }
1765         else
1766         {
1767             cl->i_state = HTTPD_CLIENT_DEAD;
1768         }
1769     }
1770     cl->i_activity_date = mdate();
1771
1772     /* XXX: for QT I have to disable timeout. Try to find why */
1773     if( cl->query.i_proto == HTTPD_PROTO_RTSP )
1774         cl->i_activity_timeout = 0;
1775
1776 #if 0 /* Debugging only */
1777     if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1778     {
1779         int i;
1780
1781         fprintf( stderr, "received new request\n" );
1782         fprintf( stderr, "  - proto=%s\n",
1783                  cl->query.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP" );
1784         fprintf( stderr, "  - version=%d\n", cl->query.i_version );
1785         fprintf( stderr, "  - msg=%d\n", cl->query.i_type );
1786         if( cl->query.i_type == HTTPD_MSG_ANSWER )
1787         {
1788             fprintf( stderr, "  - answer=%d '%s'\n", cl->query.i_status,
1789                      cl->query.psz_status );
1790         }
1791         else if( cl->query.i_type != HTTPD_MSG_NONE )
1792         {
1793             fprintf( stderr, "  - url=%s\n", cl->query.psz_url );
1794         }
1795         for( i = 0; i < cl->query.i_name; i++ )
1796         {
1797             fprintf( stderr, "  - option name='%s' value='%s'\n",
1798                      cl->query.name[i], cl->query.value[i] );
1799         }
1800     }
1801 #endif
1802 }
1803
1804 static void httpd_ClientSend( httpd_client_t *cl )
1805 {
1806     int i;
1807     int i_len;
1808
1809     if( cl->i_buffer < 0 )
1810     {
1811         /* We need to create the header */
1812         int i_size = 0;
1813         char *p;
1814
1815         i_size = strlen( "HTTP/1.") + 10 + 10 +
1816                  strlen( cl->answer.psz_status ? cl->answer.psz_status : "" ) + 5;
1817         for( i = 0; i < cl->answer.i_name; i++ )
1818         {
1819             i_size += strlen( cl->answer.name[i] ) + 2 +
1820                       strlen( cl->answer.value[i] ) + 2;
1821         }
1822
1823         if( cl->i_buffer_size < i_size )
1824         {
1825             cl->i_buffer_size = i_size;
1826             free( cl->p_buffer );
1827             cl->p_buffer = malloc( i_size );
1828         }
1829         p = (char *)cl->p_buffer;
1830
1831         p += sprintf( p, "%s/1.%d %d %s\r\n",
1832                       cl->answer.i_proto ==  HTTPD_PROTO_HTTP ? "HTTP" : "RTSP",
1833                       cl->answer.i_version,
1834                       cl->answer.i_status, cl->answer.psz_status );
1835         for( i = 0; i < cl->answer.i_name; i++ )
1836         {
1837             p += sprintf( p, "%s: %s\r\n", cl->answer.name[i],
1838                           cl->answer.value[i] );
1839         }
1840         p += sprintf( p, "\r\n" );
1841
1842         cl->i_buffer = 0;
1843         cl->i_buffer_size = (uint8_t*)p - cl->p_buffer;
1844
1845         /*fprintf( stderr, "sending answer\n" );
1846         fprintf( stderr, "%s",  cl->p_buffer );*/
1847     }
1848
1849     i_len = httpd_NetSend( cl, &cl->p_buffer[cl->i_buffer],
1850                            cl->i_buffer_size - cl->i_buffer );
1851     if( i_len >= 0 )
1852     {
1853         cl->i_activity_date = mdate();
1854         cl->i_buffer += i_len;
1855
1856         if( cl->i_buffer >= cl->i_buffer_size )
1857         {
1858             if( cl->answer.i_body == 0  && cl->answer.i_body_offset > 0 &&
1859                 !cl->b_read_waiting )
1860             {
1861                 /* catch more body data */
1862                 int     i_msg = cl->query.i_type;
1863                 int64_t i_offset = cl->answer.i_body_offset;
1864
1865                 httpd_MsgClean( &cl->answer );
1866                 cl->answer.i_body_offset = i_offset;
1867
1868                 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
1869                                           &cl->answer, &cl->query );
1870             }
1871
1872             if( cl->answer.i_body > 0 )
1873             {
1874                 /* send the body data */
1875                 free( cl->p_buffer );
1876                 cl->p_buffer = cl->answer.p_body;
1877                 cl->i_buffer_size = cl->answer.i_body;
1878                 cl->i_buffer = 0;
1879
1880                 cl->answer.i_body = 0;
1881                 cl->answer.p_body = NULL;
1882             }
1883             else
1884             {
1885                 /* send finished */
1886                 cl->i_state = HTTPD_CLIENT_SEND_DONE;
1887             }
1888         }
1889     }
1890     else
1891     {
1892 #if defined( WIN32 ) || defined( UNDER_CE )
1893         if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1894 #else
1895         if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1896 #endif
1897         {
1898             /* error */
1899             cl->i_state = HTTPD_CLIENT_DEAD;
1900         }
1901     }
1902 }
1903
1904 static void httpd_ClientTlsHsIn( httpd_client_t *cl )
1905 {
1906     switch( tls_SessionContinueHandshake( cl->p_tls ) )
1907     {
1908         case 0:
1909             cl->i_state = HTTPD_CLIENT_RECEIVING;
1910             break;
1911
1912         case -1:
1913             cl->i_state = HTTPD_CLIENT_DEAD;
1914             cl->p_tls = NULL;
1915             break;
1916
1917         case 2:
1918             cl->i_state = HTTPD_CLIENT_TLS_HS_OUT;
1919     }
1920 }
1921
1922 static void httpd_ClientTlsHsOut( httpd_client_t *cl )
1923 {
1924     switch( tls_SessionContinueHandshake( cl->p_tls ) )
1925     {
1926         case 0:
1927             cl->i_state = HTTPD_CLIENT_RECEIVING;
1928             break;
1929
1930         case -1:
1931             cl->i_state = HTTPD_CLIENT_DEAD;
1932             cl->p_tls = NULL;
1933             break;
1934
1935         case 1:
1936             cl->i_state = HTTPD_CLIENT_TLS_HS_IN;
1937             break;
1938     }
1939 }
1940
1941 static void httpd_HostThread( httpd_host_t *host )
1942 {
1943     tls_session_t *p_tls = NULL;
1944
1945     stats_Create( host, "client_connections", STATS_CLIENT_CONNECTIONS,
1946                   VLC_VAR_INTEGER, STATS_COUNTER );
1947     stats_Create( host, "active_connections", STATS_ACTIVE_CONNECTIONS,
1948                   VLC_VAR_INTEGER, STATS_COUNTER );
1949
1950     while( !host->b_die )
1951     {
1952         struct timeval  timeout;
1953         fd_set          fds_read;
1954         fd_set          fds_write;
1955         /* FIXME: (too) many int variables */
1956         int             fd, i_fd;
1957         int             i_handle_max = 0;
1958         int             i_ret;
1959         int             i_client;
1960         int             b_low_delay = 0;
1961
1962         if( host->i_url <= 0 )
1963         {
1964             /* 0.2s */
1965             msleep( 200000 );
1966             continue;
1967         }
1968
1969         /* built a set of handle to select */
1970         FD_ZERO( &fds_read );
1971         FD_ZERO( &fds_write );
1972
1973         i_handle_max = -1;
1974
1975         for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ )
1976         {
1977             FD_SET( fd, &fds_read );
1978             if( fd > i_handle_max )
1979                 i_handle_max = fd;
1980         }
1981
1982         /* prepare a new TLS session */
1983         if( ( p_tls == NULL ) && ( host->p_tls != NULL ) )
1984             p_tls = tls_ServerSessionPrepare( host->p_tls );
1985
1986         /* add all socket that should be read/write and close dead connection */
1987         vlc_mutex_lock( &host->lock );
1988         for( i_client = 0; i_client < host->i_client; i_client++ )
1989         {
1990             httpd_client_t *cl = host->client[i_client];
1991
1992             if( cl->i_ref < 0 || ( cl->i_ref == 0 &&
1993                 ( cl->i_state == HTTPD_CLIENT_DEAD ||
1994                   ( cl->i_activity_timeout > 0 &&
1995                     cl->i_activity_date+cl->i_activity_timeout < mdate()) ) ) )
1996             {
1997                 httpd_ClientClean( cl );
1998                 stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, -1, NULL );
1999                 TAB_REMOVE( host->i_client, host->client, cl );
2000                 free( cl );
2001                 i_client--;
2002                 continue;
2003             }
2004             else if( ( cl->i_state == HTTPD_CLIENT_RECEIVING )
2005                   || ( cl->i_state == HTTPD_CLIENT_TLS_HS_IN ) )
2006             {
2007                 FD_SET( cl->fd, &fds_read );
2008                 i_handle_max = __MAX( i_handle_max, cl->fd );
2009             }
2010             else if( ( cl->i_state == HTTPD_CLIENT_SENDING )
2011                   || ( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT ) )
2012             {
2013                 FD_SET( cl->fd, &fds_write );
2014                 i_handle_max = __MAX( i_handle_max, cl->fd );
2015             }
2016             else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
2017             {
2018                 httpd_message_t *answer = &cl->answer;
2019                 httpd_message_t *query  = &cl->query;
2020                 int i_msg = query->i_type;
2021
2022                 httpd_MsgInit( answer );
2023
2024                 /* Handle what we received */
2025                 if( (cl->i_mode != HTTPD_CLIENT_BIDIR) &&
2026                     (i_msg == HTTPD_MSG_ANSWER || i_msg == HTTPD_MSG_CHANNEL) )
2027                 {
2028                     /* we can only receive request from client when not
2029                      * in BIDIR mode */
2030                     cl->url     = NULL;
2031                     cl->i_state = HTTPD_CLIENT_DEAD;
2032                 }
2033                 else if( i_msg == HTTPD_MSG_ANSWER )
2034                 {
2035                     /* We are in BIDIR mode, trigger the callback and then
2036                      * check for new data */
2037                     if( cl->url && cl->url->catch[i_msg].cb )
2038                     {
2039                         cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys,
2040                                                   cl, NULL, query );
2041                     }
2042                     cl->i_state = HTTPD_CLIENT_WAITING;
2043                 }
2044                 else if( i_msg == HTTPD_MSG_CHANNEL )
2045                 {
2046                     /* We are in BIDIR mode, trigger the callback and then
2047                      * check for new data */
2048                     if( cl->url && cl->url->catch[i_msg].cb )
2049                     {
2050                         cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys,
2051                                                   cl, NULL, query );
2052                     }
2053                     cl->i_state = HTTPD_CLIENT_WAITING;
2054                 }
2055                 else if( i_msg == HTTPD_MSG_OPTIONS )
2056                 {
2057                     char *psz_cseq = NULL;
2058                     int i_cseq;
2059
2060                     /* unimplemented */
2061                     answer->i_proto  = query->i_proto ;
2062                     answer->i_type   = HTTPD_MSG_ANSWER;
2063                     answer->i_version= 0;
2064                     answer->i_status = 200;
2065                     answer->psz_status = strdup( "Ok" );
2066
2067                     answer->i_body = 0;
2068                     answer->p_body = NULL;
2069
2070                     psz_cseq = httpd_MsgGet( query, "Cseq" );
2071                     if( psz_cseq )
2072                         i_cseq = atoi( psz_cseq );
2073                     else
2074                         i_cseq = 0;
2075                     httpd_MsgAdd( answer, "Cseq", "%d", i_cseq );
2076                     httpd_MsgAdd( answer, "Server", "VLC Server" );
2077                     httpd_MsgAdd( answer, "Public", "DESCRIBE, SETUP, "
2078                                  "TEARDOWN, PLAY, PAUSE" );
2079                     httpd_MsgAdd( answer, "Content-Length", "%d",
2080                                   answer->i_body );
2081
2082                     cl->i_buffer = -1;  /* Force the creation of the answer in
2083                                          * httpd_ClientSend */
2084                     cl->i_state = HTTPD_CLIENT_SENDING;
2085                 }
2086                 else if( i_msg == HTTPD_MSG_NONE )
2087                 {
2088                     if( query->i_proto == HTTPD_PROTO_NONE )
2089                     {
2090                         cl->url = NULL;
2091                         cl->i_state = HTTPD_CLIENT_DEAD;
2092                     }
2093                     else
2094                     {
2095                         uint8_t *p;
2096
2097                         /* unimplemented */
2098                         answer->i_proto  = query->i_proto ;
2099                         answer->i_type   = HTTPD_MSG_ANSWER;
2100                         answer->i_version= 0;
2101                         answer->i_status = 501;
2102                         answer->psz_status = strdup( "Unimplemented" );
2103
2104                         p = answer->p_body = malloc( 1000 );
2105
2106                         p += sprintf( (char *)p,
2107                             "<?xml version=\"1.0\" encoding=\"ascii\" ?>"
2108                             "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2109                             "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2110                             "<html>\n"
2111                             "<head>\n"
2112                             "<title>Error 501</title>\n"
2113                             "</head>\n"
2114                             "<body>\n"
2115                             "<h1>501 Unimplemented</h1>\n"
2116                             "<hr />\n"
2117                             "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
2118                             "</body>\n"
2119                             "</html>\n" );
2120
2121                         answer->i_body = p - answer->p_body;
2122                         httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
2123
2124                         cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
2125                         cl->i_state = HTTPD_CLIENT_SENDING;
2126                     }
2127                 }
2128                 else
2129                 {
2130                     vlc_bool_t b_auth_failed = VLC_FALSE;
2131                     vlc_bool_t b_hosts_failed = VLC_FALSE;
2132                     int i;
2133
2134                     /* Search the url and trigger callbacks */
2135                     for( i = 0; i < host->i_url; i++ )
2136                     {
2137                         httpd_url_t *url = host->url[i];
2138
2139                         if( !strcmp( url->psz_url, query->psz_url ) )
2140                         {
2141                             if( url->catch[i_msg].cb )
2142                             {
2143                                 if( answer && ( url->p_acl != NULL ) )
2144                                 {
2145                                     char ip[NI_MAXNUMERICHOST];
2146
2147                                     if( httpd_ClientIP( cl, ip ) != NULL )
2148                                     {
2149                                         if( ACL_Check( url->p_acl, ip ) )
2150                                         {
2151                                             b_hosts_failed = VLC_TRUE;
2152                                             break;
2153                                         }
2154                                     }
2155                                     else
2156                                         b_hosts_failed = VLC_TRUE;
2157                                 }
2158
2159                                 if( answer && ( *url->psz_user || *url->psz_password ) )
2160                                 {
2161                                     /* create the headers */
2162                                     char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */
2163                                     char *auth;
2164                                     char *id;
2165
2166                                     asprintf( &id, "%s:%s", url->psz_user, url->psz_password );
2167                                     if( b64 ) auth = malloc( strlen(b64) + 1 );
2168                                     else auth = malloc( strlen("") + 1 );
2169
2170                                     if( b64 != NULL
2171                                          && !strncasecmp( b64, "BASIC", 5 ) )
2172                                     {
2173                                         b64 += 5;
2174                                         while( *b64 == ' ' )
2175                                         {
2176                                             b64++;
2177                                         }
2178                                         b64_decode( auth, b64 );
2179                                     }
2180                                     else
2181                                     {
2182                                         strcpy( auth, "" );
2183                                     }
2184
2185                                     if( strcmp( id, auth ) )
2186                                     {
2187                                         httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user );
2188                                         /* We fail for all url */
2189                                         b_auth_failed = VLC_TRUE;
2190                                         free( id );
2191                                         free( auth );
2192                                         break;
2193                                     }
2194
2195                                     free( id );
2196                                     free( auth );
2197                                 }
2198
2199                                 if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) )
2200                                 {
2201                                     if( answer->i_proto == HTTPD_PROTO_NONE )
2202                                     {
2203                                         /* Raw answer from a CGI */
2204                                         cl->i_buffer = cl->i_buffer_size;
2205                                     }
2206                                     else
2207                                         cl->i_buffer = -1;
2208
2209                                     /* only one url can answer */
2210                                     answer = NULL;
2211                                     if( cl->url == NULL )
2212                                     {
2213                                         cl->url = url;
2214                                     }
2215                                 }
2216                             }
2217                         }
2218                     }
2219
2220                     if( answer )
2221                     {
2222                         uint8_t *p;
2223
2224                         answer->i_proto  = query->i_proto;
2225                         answer->i_type   = HTTPD_MSG_ANSWER;
2226                         answer->i_version= 0;
2227                         p = answer->p_body = malloc( 1000 + strlen(query->psz_url) );
2228
2229                         if( b_hosts_failed )
2230                         {
2231                             answer->i_status = 403;
2232                             answer->psz_status = strdup( "Forbidden" );
2233
2234                             /* FIXME: lots of code duplication */
2235                             p += sprintf( (char *)p,
2236                                 "<?xml version=\"1.0\" encoding=\"us-ascii\" ?>"
2237                                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2238                                 "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2239                                 "<html>\n"
2240                                 "<head>\n"
2241                                 "<title>Error 403</title>\n"
2242                                 "</head>\n"
2243                                 "<body>\n"
2244                                 "<h1>403 Forbidden (%s)</h1>\n"
2245                                 "<hr />\n"
2246                                 "<p><a href=\"http://www.videolan.org\">VideoLAN</a></p>\n"
2247                                 "</body>\n"
2248                                 "</html>\n", query->psz_url );
2249                         }
2250                         else if( b_auth_failed )
2251                         {
2252                             answer->i_status = 401;
2253                             answer->psz_status = strdup( "Authorization Required" );
2254
2255                             p += sprintf( (char *)p,
2256                                 "<?xml version=\"1.0\" encoding=\"us-ascii\" ?>"
2257                                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2258                                 "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2259                                 "<html>\n"
2260                                 "<head>\n"
2261                                 "<title>Error 401</title>\n"
2262                                 "</head>\n"
2263                                 "<body>\n"
2264                                 "<h1>401 Authorization Required (%s)</h1>\n"
2265                                 "<hr />\n"
2266                                 "<p><a href=\"http://www.videolan.org\">VideoLAN</a></p>\n"
2267                                 "</body>\n"
2268                                 "</html>\n", query->psz_url );
2269                         }
2270                         else
2271                         {
2272                             /* no url registered */
2273                             answer->i_status = 404;
2274                             answer->psz_status = strdup( "Not found" );
2275
2276                             p += sprintf( (char *)p,
2277                                 "<?xml version=\"1.0\" encoding=\"us-ascii\" ?>"
2278                                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD  XHTML 1.0 Strict//EN\" "
2279                                 "\"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
2280                                 "<html>\n"
2281                                 "<head>\n"
2282                                 "<title>Error 404</title>\n"
2283                                 "</head>\n"
2284                                 "<body>\n"
2285                                 "<h1>404 Resource not found(%s)</h1>\n"
2286                                 "<hr />\n"
2287                                 "<p><a href=\"http://www.videolan.org\"> VideoLAN</a></p>\n"
2288                                 "</body>\n"
2289                                 "</html>\n", query->psz_url );
2290                         }
2291
2292                         answer->i_body = p - answer->p_body;
2293                         cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
2294                         httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
2295                         httpd_MsgAdd( answer, "Content-Type", "%s", "text/html" );
2296                     }
2297
2298                     cl->i_state = HTTPD_CLIENT_SENDING;
2299                 }
2300             }
2301             else if( cl->i_state == HTTPD_CLIENT_SEND_DONE )
2302             {
2303                 if( cl->i_mode == HTTPD_CLIENT_FILE || cl->answer.i_body_offset == 0 )
2304                 {
2305                     char *psz_connection = httpd_MsgGet( &cl->answer, "Connection" );
2306                     char *psz_query = httpd_MsgGet( &cl->query, "Connection" );
2307                     vlc_bool_t b_connection = VLC_FALSE;
2308                     vlc_bool_t b_keepalive = VLC_FALSE;
2309                     vlc_bool_t b_query = VLC_FALSE;
2310
2311                     cl->url = NULL;
2312                     if( psz_connection )
2313                     {
2314                         b_connection = ( strcasecmp( psz_connection, "Close" ) == 0 );
2315                         b_keepalive = ( strcasecmp( psz_connection, "Keep-Alive" ) == 0 );
2316                     }
2317
2318                     if( psz_query )
2319                     {
2320                         b_query = ( strcasecmp( psz_query, "Close" ) == 0 );
2321                     }
2322
2323                     if( ( ( cl->query.i_proto == HTTPD_PROTO_HTTP ) &&
2324                           ( ( cl->answer.i_version == 0 && b_keepalive ) ||
2325                             ( cl->answer.i_version == 1 && !b_connection ) ) ) ||
2326                         ( ( cl->query.i_proto == HTTPD_PROTO_RTSP ) &&
2327                           !b_query && !b_connection ) )
2328                     {
2329                         httpd_MsgClean( &cl->query );
2330                         httpd_MsgInit( &cl->query );
2331
2332                         cl->i_buffer = 0;
2333                         cl->i_buffer_size = 1000;
2334                         free( cl->p_buffer );
2335                         cl->p_buffer = malloc( cl->i_buffer_size );
2336                         cl->i_state = HTTPD_CLIENT_RECEIVING;
2337                     }
2338                     else
2339                     {
2340                         cl->i_state = HTTPD_CLIENT_DEAD;
2341                     }
2342                     httpd_MsgClean( &cl->answer );
2343                 }
2344                 else if( cl->b_read_waiting )
2345                 {
2346                     /* we have a message waiting for us to read it */
2347                     httpd_MsgClean( &cl->answer );
2348                     httpd_MsgClean( &cl->query );
2349
2350                     cl->i_buffer = 0;
2351                     cl->i_buffer_size = 1000;
2352                     free( cl->p_buffer );
2353                     cl->p_buffer = malloc( cl->i_buffer_size );
2354                     cl->i_state = HTTPD_CLIENT_RECEIVING;
2355                     cl->b_read_waiting = VLC_FALSE;
2356                 }
2357                 else
2358                 {
2359                     int64_t i_offset = cl->answer.i_body_offset;
2360                     httpd_MsgClean( &cl->answer );
2361
2362                     cl->answer.i_body_offset = i_offset;
2363                     free( cl->p_buffer );
2364                     cl->p_buffer = NULL;
2365                     cl->i_buffer = 0;
2366                     cl->i_buffer_size = 0;
2367
2368                     cl->i_state = HTTPD_CLIENT_WAITING;
2369                 }
2370             }
2371             else if( cl->i_state == HTTPD_CLIENT_WAITING )
2372             {
2373                 int64_t i_offset = cl->answer.i_body_offset;
2374                 int     i_msg = cl->query.i_type;
2375
2376                 httpd_MsgInit( &cl->answer );
2377                 cl->answer.i_body_offset = i_offset;
2378
2379                 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
2380                                           &cl->answer, &cl->query );
2381                 if( cl->answer.i_type != HTTPD_MSG_NONE )
2382                 {
2383                     /* we have new data, so reenter send mode */
2384                     cl->i_buffer      = 0;
2385                     cl->p_buffer      = cl->answer.p_body;
2386                     cl->i_buffer_size = cl->answer.i_body;
2387                     cl->answer.p_body = NULL;
2388                     cl->answer.i_body = 0;
2389                     cl->i_state = HTTPD_CLIENT_SENDING;
2390                 }
2391                 else
2392                 {
2393                     /* we shouldn't wait too long */
2394                     b_low_delay = VLC_TRUE;
2395                 }
2396             }
2397
2398             /* Special for BIDIR mode we also check reading */
2399             if( cl->i_mode == HTTPD_CLIENT_BIDIR &&
2400                 cl->i_state == HTTPD_CLIENT_SENDING )
2401             {
2402                 FD_SET( cl->fd, &fds_read );
2403                 i_handle_max = __MAX( i_handle_max, cl->fd );
2404             }
2405         }
2406         vlc_mutex_unlock( &host->lock );
2407
2408         /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */
2409         timeout.tv_sec = 0;
2410         timeout.tv_usec = b_low_delay ? 20000 : 100000;
2411
2412         i_ret = select( i_handle_max + 1,
2413                         &fds_read, &fds_write, NULL, &timeout );
2414
2415         if( (i_ret == -1) && (errno != EINTR) )
2416         {
2417 #if defined(WIN32) || defined(UNDER_CE)
2418             msg_Warn( host, "cannot select sockets (%d)", WSAGetLastError( ) );
2419 #else
2420             msg_Warn( host, "cannot select sockets : %s", strerror( errno ) );
2421 #endif
2422             msleep( 1000 );
2423             continue;
2424         }
2425         else if( i_ret <= 0 )
2426         {
2427             continue;
2428         }
2429
2430         /* accept new connections */
2431         for( i_fd = 0; (fd = host->fd[i_fd]) != -1; i_fd++ )
2432         {
2433             if( FD_ISSET( fd, &fds_read ) )
2434             {
2435                 socklen_t i_sock_size = sizeof( struct sockaddr_storage );
2436                 struct  sockaddr_storage sock;
2437
2438                 // FIXME: use net_Accept()
2439                 fd = accept( fd, (struct sockaddr *)&sock, &i_sock_size );
2440                 if( fd >= FD_SETSIZE )
2441                 {
2442                     net_Close( fd );
2443                     fd = -1;
2444                 }
2445
2446                 if( fd >= 0 )
2447                 {
2448                     int i_state = 0;
2449
2450                     /* set this new socket non-block */
2451 #if defined( WIN32 ) || defined( UNDER_CE )
2452                     {
2453                         unsigned long i_dummy = 1;
2454                         ioctlsocket( fd, FIONBIO, &i_dummy );
2455                     }
2456 #else
2457                     fcntl( fd, F_SETFL, O_NONBLOCK );
2458 #endif
2459
2460                     if( p_tls != NULL)
2461                     {
2462                         switch ( tls_ServerSessionHandshake( p_tls, fd ) )
2463                         {
2464                             case -1:
2465                                 msg_Err( host, "Rejecting TLS connection" );
2466                                 net_Close( fd );
2467                                 fd = -1;
2468                                 p_tls = NULL;
2469                                 break;
2470
2471                             case 1: /* missing input - most likely */
2472                                 i_state = HTTPD_CLIENT_TLS_HS_IN;
2473                                 break;
2474
2475                             case 2: /* missing output */
2476                                 i_state = HTTPD_CLIENT_TLS_HS_OUT;
2477                                 break;
2478                         }
2479                     }
2480
2481                     if( fd >= 0 )
2482                     {
2483                         httpd_client_t *cl;
2484                         char ip[NI_MAXNUMERICHOST];
2485                         stats_UpdateInteger( host, STATS_CLIENT_CONNECTIONS,
2486                                              1, NULL );
2487                         stats_UpdateInteger( host, STATS_ACTIVE_CONNECTIONS, 1,
2488                                              NULL );
2489                         cl = httpd_ClientNew( fd, &sock, i_sock_size, p_tls );
2490                         httpd_ClientIP( cl, ip );
2491                         msg_Dbg( host, "Connection from %s", ip );
2492                         p_tls = NULL;
2493                         vlc_mutex_lock( &host->lock );
2494                         TAB_APPEND( host->i_client, host->client, cl );
2495                         vlc_mutex_unlock( &host->lock );
2496
2497                         if( i_state != 0 )
2498                             cl->i_state = i_state; // override state for TLS
2499                     }
2500                 }
2501             }
2502         }
2503
2504         /* now try all others socket */
2505         vlc_mutex_lock( &host->lock );
2506         for( i_client = 0; i_client < host->i_client; i_client++ )
2507         {
2508             httpd_client_t *cl = host->client[i_client];
2509             if( cl->i_state == HTTPD_CLIENT_RECEIVING )
2510             {
2511                 httpd_ClientRecv( cl );
2512             }
2513             else if( cl->i_state == HTTPD_CLIENT_SENDING )
2514             {
2515                 httpd_ClientSend( cl );
2516             }
2517             else if( cl->i_state == HTTPD_CLIENT_TLS_HS_IN )
2518             {
2519                 httpd_ClientTlsHsIn( cl );
2520             }
2521             else if( cl->i_state == HTTPD_CLIENT_TLS_HS_OUT )
2522             {
2523                 httpd_ClientTlsHsOut( cl );
2524             }
2525
2526             if( cl->i_mode == HTTPD_CLIENT_BIDIR &&
2527                 cl->i_state == HTTPD_CLIENT_SENDING &&
2528                 FD_ISSET( cl->fd, &fds_read ) )
2529             {
2530                 cl->b_read_waiting = VLC_TRUE;
2531             }
2532         }
2533         vlc_mutex_unlock( &host->lock );
2534     }
2535
2536     if( p_tls != NULL )
2537         tls_ServerSessionClose( p_tls );
2538 }
2539
2540 #else /* ENABLE_HTTPD */
2541
2542 /* We just define an empty wrapper */
2543 httpd_host_t *httpd_TLSHostNew( vlc_object_t *a, char *b, int c,
2544                                 tls_server_t *d )
2545 {
2546     msg_Err( a, "HTTP daemon support is disabled" );
2547     return NULL;
2548 }
2549
2550 httpd_host_t *httpd_HostNew( vlc_object_t *a, char *b, int c )
2551 {
2552     msg_Err( a, "HTTP daemon support is disabled" );
2553     return NULL;
2554 }
2555
2556 void httpd_HostDelete( httpd_host_t *a )
2557 {
2558 }
2559
2560 httpd_url_t *httpd_UrlNew( httpd_host_t *host, char *psz_url,
2561                            char *psz_user, char *psz_password,
2562                            const vlc_acl_t *p_acl )
2563 {
2564     return NULL;
2565 }
2566
2567 httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, char *psz_url,
2568                                  char *psz_user, char *psz_password,
2569                                  const vlc_acl_t *p_acl )
2570 {
2571     return NULL;
2572 }
2573
2574 int httpd_UrlCatch( httpd_url_t *a, int b, httpd_callback_t c,
2575                     httpd_callback_sys_t *d )
2576 {
2577     return 0;
2578 }
2579
2580 void httpd_UrlDelete( httpd_url_t *a )
2581 {
2582 }
2583
2584 char* httpd_ClientIP( httpd_client_t *cl, char *psz_ip )
2585 {
2586     return NULL;
2587 }
2588
2589 char* httpd_ServerIP( httpd_client_t *cl, char *psz_ip )
2590 {
2591     return NULL;
2592 }
2593
2594 void httpd_ClientModeStream( httpd_client_t *a )
2595 {
2596 }
2597
2598 void httpd_ClientModeBidir( httpd_client_t *a )
2599 {
2600 }
2601
2602 void httpd_FileDelete( httpd_file_t *a )
2603 {
2604 }
2605
2606 httpd_file_t *httpd_FileNew( httpd_host_t *a, char *b, char *c, char *d,
2607                              char *e, httpd_file_callback_t f,
2608                              httpd_file_sys_t *g )
2609 {
2610     return NULL;
2611 }
2612
2613 httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
2614                                    const char *psz_user,
2615                                    const char *psz_password,
2616                                    const vlc_acl_t *p_acl,
2617                                    httpd_handler_callback_t pf_fill,
2618                                    httpd_handler_sys_t *p_sys )
2619 {
2620     return NULL;
2621 }
2622
2623 void httpd_HandlerDelete( httpd_handler_t *handler )
2624 {
2625 }
2626
2627 void httpd_RedirectDelete( httpd_redirect_t *a )
2628 {
2629 }
2630
2631 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *a,
2632                                      char *b, char *c )
2633 {
2634     return NULL;
2635 }
2636
2637 void httpd_StreamDelete( httpd_stream_t *a )
2638 {
2639 }
2640
2641 int httpd_StreamHeader( httpd_stream_t *a, uint8_t *b, int c )
2642 {
2643     return 0;
2644 }
2645
2646 int httpd_StreamSend ( httpd_stream_t *a, uint8_t *b, int c )
2647 {
2648     return 0;
2649 }
2650
2651 httpd_stream_t *httpd_StreamNew( httpd_host_t *a, char *b, char *c,
2652                                  char *d, char *e )
2653 {
2654     return NULL;
2655 }
2656
2657 void httpd_MsgInit ( httpd_message_t *a )
2658 {
2659 }
2660
2661 void httpd_MsgAdd  ( httpd_message_t *a, char *b, char *c, ... )
2662 {
2663 }
2664
2665 char *httpd_MsgGet ( httpd_message_t *a, char *b )
2666 {
2667     return NULL;
2668 }
2669
2670 void httpd_MsgClean( httpd_message_t *a )
2671 {
2672 }
2673
2674 #endif /* ENABLE_HTTPD */