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