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