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