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