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