]> git.sesse.net Git - vlc/blob - src/misc/httpd.c
* include/httpd.h, modules/misc/httpd.c: remove old http daemon.
[vlc] / src / misc / httpd.c
1 /*****************************************************************************
2  * httpd.c
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id: httpd.c,v 1.1 2004/03/03 13:23:47 fenrir Exp $
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     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
453
454     return VLC_SUCCESS;
455 }
456
457
458 httpd_file_t *httpd_FileNew( httpd_host_t *host,
459                              char *psz_url, char *psz_mime,
460                              char *psz_user, char *psz_password,
461                              httpd_file_callback_t pf_fill,
462                              httpd_file_sys_t *p_sys )
463 {
464     httpd_file_t *file = malloc( sizeof( httpd_file_t ) );
465
466     if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user, psz_password ) ) == NULL )
467     {
468         free( file );
469         return NULL;
470     }
471
472     file->psz_url  = strdup( psz_url );
473     if( psz_mime && *psz_mime )
474     {
475         file->psz_mime = strdup( psz_mime );
476     }
477     else
478     {
479         file->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
480     }
481
482     file->pf_fill = pf_fill;
483     file->p_sys   = p_sys;
484
485     httpd_UrlCatch( file->url, HTTPD_MSG_HEAD, httpd_FileCallBack, (httpd_callback_sys_t*)file );
486     httpd_UrlCatch( file->url, HTTPD_MSG_GET,  httpd_FileCallBack, (httpd_callback_sys_t*)file );
487     httpd_UrlCatch( file->url, HTTPD_MSG_POST, httpd_FileCallBack, (httpd_callback_sys_t*)file );
488
489     return file;
490 }
491
492 void         httpd_FileDelete( httpd_file_t *file )
493 {
494     httpd_UrlDelete( file->url );
495
496     free( file->psz_url );
497     free( file->psz_mime );
498
499     free( file );
500 }
501
502 /*****************************************************************************
503  * High Level Funtions: httpd_redirect_t
504  *****************************************************************************/
505 struct httpd_redirect_t
506 {
507     httpd_url_t *url;
508     char        *psz_dst;
509 };
510
511 static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query )
512 {
513     httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys;
514     uint8_t *p;
515
516     if( answer == NULL || query == NULL )
517     {
518         return VLC_SUCCESS;
519     }
520     answer->i_proto  = query->i_proto;
521     answer->i_version= query->i_version;
522     answer->i_type   = HTTPD_MSG_ANSWER;
523     answer->i_status = 301;
524     answer->psz_status = strdup( "Moved Permanently" );
525
526     p = answer->p_body = malloc( 1000 + strlen( rdir->psz_dst ) );
527     p += sprintf( p, "<html>\n" );
528     p += sprintf( p, "<head>\n" );
529     p += sprintf( p, "<title>Redirection</title>\n" );
530     p += sprintf( p, "</head>\n" );
531     p += sprintf( p, "<body>\n" );
532     p += sprintf( p, "<h1><center>You should be <a href=\"%s\">redirected</a></center></h1>\n", rdir->psz_dst );
533     p += sprintf( p, "<hr />\n" );
534     p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
535     p += sprintf( p, "</body>\n" );
536     p += sprintf( p, "</html>\n" );
537     answer->i_body = p - answer->p_body;
538
539     /* XXX check if it's ok or we need to set an absolute url */
540     httpd_MsgAdd( answer, "Location",  "%s", rdir->psz_dst );
541
542     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
543
544     return VLC_SUCCESS;
545 }
546
547 httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, char *psz_url_dst, char *psz_url_src )
548 {
549     httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) );
550
551     if( ( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL ) ) == NULL )
552     {
553         free( rdir );
554         return NULL;
555     }
556     rdir->psz_dst = strdup( psz_url_dst );
557     /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
558     httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD,      httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir );
559     httpd_UrlCatch( rdir->url, HTTPD_MSG_GET,       httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir );
560     httpd_UrlCatch( rdir->url, HTTPD_MSG_POST,      httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir );
561
562     httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE,  httpd_RedirectCallBack, (httpd_callback_sys_t*)rdir );
563
564     return rdir;
565 }
566 void              httpd_RedirectDelete( httpd_redirect_t *rdir )
567 {
568     httpd_UrlDelete( rdir->url );
569     free( rdir->psz_dst );
570     free( rdir );
571 }
572
573 /*****************************************************************************
574  * High Level Funtions: httpd_stream_t
575  *****************************************************************************/
576 struct httpd_stream_t
577 {
578     vlc_mutex_t lock;
579     httpd_url_t *url;
580
581     char    *psz_mime;
582
583     /* Header to send as first packet */
584     uint8_t *p_header;
585     int     i_header;
586
587     /* circular buffer */
588     int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
589     uint8_t     *p_buffer;          /* buffer */
590     int64_t     i_buffer_pos;       /* absolute position from begining */
591     int64_t     i_buffer_last_pos;  /* a new connection will start with that */
592 };
593
594 static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl, httpd_message_t *answer, httpd_message_t *query )
595 {
596     httpd_stream_t *stream = (httpd_stream_t*)p_sys;
597
598     if( answer == NULL || query == NULL || cl == NULL )
599     {
600         return VLC_SUCCESS;
601     }
602     if( answer->i_body_offset > 0 )
603     {
604         int64_t i_write;
605         int     i_pos;
606
607         /* fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n", answer->i_body_offset ); */
608
609         if( answer->i_body_offset >= stream->i_buffer_pos )
610         {
611             /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */
612             return VLC_EGENERIC;    /* wait, no data available */
613         }
614         if( answer->i_body_offset + stream->i_buffer_size < stream->i_buffer_pos )
615         {
616             /* this client isn't fast enough */
617             fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n",
618                      answer->i_body_offset, stream->i_buffer_last_pos );
619             answer->i_body_offset = stream->i_buffer_last_pos;
620         }
621
622         i_pos   = answer->i_body_offset % stream->i_buffer_size;
623         i_write = stream->i_buffer_pos - answer->i_body_offset;
624         if( i_write > 10000 )
625         {
626             i_write = 10000;
627         }
628         else if( i_write <= 0 )
629         {
630             return VLC_EGENERIC;    /* wait, no data available */
631         }
632
633         /* using HTTPD_MSG_ANSWER -> data available */
634         answer->i_proto  = HTTPD_PROTO_HTTP;
635         answer->i_version= 0;
636         answer->i_type   = HTTPD_MSG_ANSWER;
637
638         answer->i_body = i_write;
639         answer->p_body = malloc( i_write );
640         memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write );
641
642         answer->i_body_offset += i_write;
643
644         return VLC_SUCCESS;
645     }
646     else
647     {
648         answer->i_proto  = HTTPD_PROTO_HTTP;
649         answer->i_version= 0;
650         answer->i_type   = HTTPD_MSG_ANSWER;
651
652         answer->i_status = 200;
653         answer->psz_status = strdup( "OK" );
654
655         if( query->i_type != HTTPD_MSG_HEAD )
656         {
657             httpd_ClientModeStream( cl );
658             vlc_mutex_lock( &stream->lock );
659             /* Send the header */
660             if( stream->i_header > 0 )
661             {
662                 answer->i_body = stream->i_header;
663                 answer->p_body = malloc( stream->i_header );
664                 memcpy( answer->p_body, stream->p_header, stream->i_header );
665             }
666             answer->i_body_offset = stream->i_buffer_last_pos;
667             vlc_mutex_unlock( &stream->lock );
668         }
669         else
670         {
671             httpd_MsgAdd( answer, "Content-Length", "%d", 0 );
672         }
673
674         if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) )
675         {
676             vlc_bool_t b_xplaystream = VLC_FALSE;
677             int i;
678
679             httpd_MsgAdd( answer, "Content-type", "%s", "application/octet-stream" );
680             httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" );
681             httpd_MsgAdd( answer, "Pragma", "no-cache" );
682             httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff );
683             httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" );
684
685             /* Check if there is a xPlayStrm=1 */
686             for( i = 0; i < query->i_name; i++ )
687             {
688                 if( !strcasecmp( query->name[i],  "Pragma" ) &&
689                     !strcasecmp( query->value[i], "xPlayStrm=1" ) )
690                 {
691                     b_xplaystream = VLC_TRUE;
692                 }
693             }
694
695             if( !b_xplaystream )
696             {
697                 answer->i_body_offset = 0;
698             }
699         }
700         else
701         {
702             httpd_MsgAdd( answer, "Content-type",  "%s", stream->psz_mime );
703         }
704         httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
705         return VLC_SUCCESS;
706     }
707 }
708
709 httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
710                                  char *psz_url, char *psz_mime,
711                                  char *psz_user, char *psz_password )
712 {
713     httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) );
714
715     if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user, psz_password ) ) == NULL )
716     {
717         free( stream );
718         return NULL;
719     }
720     vlc_mutex_init( host, &stream->lock );
721     if( psz_mime && *psz_mime )
722     {
723         stream->psz_mime = strdup( psz_mime );
724     }
725     else
726     {
727         stream->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
728     }
729     stream->i_header = 0;
730     stream->p_header = NULL;
731     stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
732     stream->p_buffer = malloc( stream->i_buffer_size );
733     /* We set to 1, to make life simpler (this way i_body_offset can never be 0) */
734     stream->i_buffer_pos = 1;
735     stream->i_buffer_last_pos = 1;
736
737     httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack, (httpd_callback_sys_t*)stream );
738     httpd_UrlCatch( stream->url, HTTPD_MSG_GET,  httpd_StreamCallBack, (httpd_callback_sys_t*)stream );
739     httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack, (httpd_callback_sys_t*)stream );
740
741     return stream;
742 }
743
744 int  httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data )
745 {
746     vlc_mutex_lock( &stream->lock );
747     if( stream->p_header )
748     {
749         free( stream->p_header );
750         stream->p_header = NULL;
751     }
752     stream->i_header = i_data;
753     if( i_data > 0 )
754     {
755         stream->p_header = malloc( i_data );
756         memcpy( stream->p_header, p_data, i_data );
757     }
758     vlc_mutex_unlock( &stream->lock );
759
760     return VLC_SUCCESS;
761 }
762
763 int  httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data )
764 {
765     int i_count;
766     int i_pos;
767
768     if( i_data < 0 || p_data == NULL )
769     {
770         return VLC_SUCCESS;
771     }
772     vlc_mutex_lock( &stream->lock );
773
774     /* save this pointer (to be used by new connection) */
775     stream->i_buffer_last_pos = stream->i_buffer_pos;
776
777     i_pos = stream->i_buffer_pos % stream->i_buffer_size;
778     i_count = i_data;
779     while( i_count > 0)
780     {
781         int i_copy;
782
783         i_copy = __MIN( i_count, stream->i_buffer_size - i_pos );
784
785         /* Ok, we can't go past the end of our buffer */
786         memcpy( &stream->p_buffer[i_pos], p_data, i_copy );
787
788         i_pos = ( i_pos + i_copy ) % stream->i_buffer_size;
789         i_count -= i_copy;
790         p_data  += i_copy;
791     }
792
793     stream->i_buffer_pos += i_data;
794
795     vlc_mutex_unlock( &stream->lock );
796     return VLC_SUCCESS;
797 }
798
799 void httpd_StreamDelete( httpd_stream_t *stream )
800 {
801     httpd_UrlDelete( stream->url );
802     vlc_mutex_destroy( &stream->lock );
803     if( stream->psz_mime ) free( stream->psz_mime );
804     if( stream->p_header ) free( stream->p_header );
805     if( stream->p_buffer ) free( stream->p_buffer );
806     free( stream );
807 }
808
809
810 /*****************************************************************************
811  * Low level
812  *****************************************************************************/
813 #define LISTEN_BACKLOG          100
814
815 #if defined( WIN32 ) || defined( UNDER_CE )
816 #define SOCKET_CLOSE(a)    closesocket(a)
817 #else
818 #define SOCKET_CLOSE(a)    close(a)
819 #endif
820
821 static void httpd_HostThread( httpd_host_t * );
822 static int BuildAddr( struct sockaddr_in * p_socket,
823                       const char * psz_address, int i_port );
824
825
826 /* create a new host */
827 httpd_host_t *httpd_HostNew( vlc_object_t *p_this, char *psz_host, int i_port )
828 {
829     httpd_t      *httpd;
830     httpd_host_t *host;
831     vlc_value_t lockval;
832     struct sockaddr_in sock;
833     int i;
834
835     /* resolv */
836     if( BuildAddr( &sock, psz_host, i_port ) )
837     {
838         msg_Err( p_this, "cannot build address for %s:%d", psz_host, i_port );
839         return NULL;
840     }
841
842     /* to be sure to avoid multiple creation */
843     var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX );
844     var_Get( p_this->p_libvlc, "httpd_mutex", &lockval );
845     vlc_mutex_lock( lockval.p_address );
846
847     if( ( httpd = vlc_object_find( p_this, VLC_OBJECT_HTTPD, FIND_ANYWHERE ) ) == NULL )
848     {
849         msg_Info( p_this, "creating httpd" );
850         if( ( httpd = vlc_object_create( p_this, VLC_OBJECT_HTTPD ) ) == NULL )
851         {
852             vlc_mutex_unlock( lockval.p_address );
853             return NULL;
854         }
855
856         httpd->i_host = 0;
857         httpd->host   = NULL;
858
859         vlc_object_yield( httpd );
860         vlc_object_attach( httpd, p_this->p_vlc );
861     }
862
863     /* verify if it already exist */
864     for( i = 0; i < httpd->i_host; i++ )
865     {
866         if( httpd->host[i]->sock.sin_port == sock.sin_port &&
867             ( httpd->host[i]->sock.sin_addr.s_addr == INADDR_ANY ||
868               httpd->host[i]->sock.sin_addr.s_addr == sock.sin_addr.s_addr ) )
869         {
870             /* yep found */
871             host = httpd->host[i];
872             host->i_ref++;
873
874             vlc_mutex_unlock( lockval.p_address );
875
876             msg_Dbg( p_this, "host already registered" );
877             return host;
878         }
879     }
880     /* create the new host */
881     host = vlc_object_create( p_this, sizeof( httpd_host_t ) );
882     host->httpd = httpd;
883     vlc_mutex_init( httpd, &host->lock );
884     host->i_ref = 1;
885     memcpy( &host->sock, &sock, sizeof( struct sockaddr_in ) );
886     host->i_url     = 0;
887     host->url       = NULL;
888     host->i_client  = 0;
889     host->client    = NULL;
890
891     /* create the listening socket */
892     if( ( host->fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
893     {
894         goto socket_error;
895     }
896     /* reuse socket */
897     i = 1;
898     if( setsockopt( host->fd, SOL_SOCKET, SO_REUSEADDR,
899                     (void *) &i, sizeof( i ) ) < 0 )
900     {
901         msg_Warn( p_this, "cannot configure socket (SO_REUSEADDR)" );
902     }
903     /* bind it */
904     if( bind( host->fd, (struct sockaddr *)&host->sock, sizeof( struct sockaddr_in ) ) < 0 )
905     {
906         msg_Err( p_this, "cannot bind socket" );
907         goto socket_error;
908     }
909     /* set to non-blocking */
910 #if defined( WIN32 ) || defined( UNDER_CE )
911     {
912         unsigned long i_dummy = 1;
913         if( ioctlsocket( host->fd, FIONBIO, &i_dummy ) != 0 )
914         {
915             msg_Err( p_this, "cannot set socket to non-blocking mode" );
916             goto socket_error;
917         }
918     }
919 #else
920     {
921         unsigned int i_flags;
922         if( ( i_flags = fcntl( host->fd, F_GETFL, 0 ) ) < 0 )
923         {
924             msg_Err( p_this, "cannot F_GETFL socket" );
925             goto socket_error;
926         }
927         if( fcntl( host->fd, F_SETFL, i_flags | O_NONBLOCK ) < 0 )
928         {
929             msg_Err( p_this, "cannot F_SETFL O_NONBLOCK" );
930             goto socket_error;
931         }
932     }
933 #endif
934     /* listen */
935     if( listen( host->fd, LISTEN_BACKLOG ) < 0 )
936     {
937         msg_Err( p_this, "cannot listen socket" );
938         goto socket_error;
939     }
940
941     /* create the thread */
942     if( vlc_thread_create( host, "httpd host thread",
943                            httpd_HostThread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
944     {
945         msg_Err( p_this, "cannot spawn http host thread" );
946         goto socket_error;
947     }
948
949     /* now add it to httpd */
950     TAB_APPEND( httpd->i_host, httpd->host, host );
951     vlc_mutex_unlock( lockval.p_address );
952
953     return host;
954
955 socket_error:
956     vlc_mutex_unlock( lockval.p_address );
957
958     if( host->fd > 0 )
959     {
960         SOCKET_CLOSE( host->fd );
961     }
962     vlc_mutex_destroy( &host->lock );
963     vlc_object_destroy( host );
964
965     /* TODO destroy no more used httpd TODO */
966     vlc_object_release( httpd );
967     return NULL;
968 }
969
970 /* delete a host */
971 void          httpd_HostDelete( httpd_host_t *host )
972 {
973     httpd_t *httpd = host->httpd;
974     vlc_value_t lockval;
975     int i;
976
977     msg_Dbg( host, "httpd_HostDelete" );
978
979     var_Get( httpd->p_libvlc, "httpd_mutex", &lockval );
980     vlc_mutex_lock( lockval.p_address );
981
982     vlc_object_release( httpd );
983
984     host->i_ref--;
985     if( host->i_ref > 0 )
986     {
987         /* still used */
988         vlc_mutex_unlock( lockval.p_address );
989         msg_Dbg( host, "httpd_HostDelete: host still used" );
990         return;
991     }
992     TAB_REMOVE( httpd->i_host, httpd->host, host );
993
994     msg_Dbg( host, "httpd_HostDelete: host removed from http" );
995
996     host->b_die = 1;
997     vlc_thread_join( host );
998
999     msg_Dbg( host, "httpd_HostDelete: host thread joined" );
1000
1001     for( i = 0; i < host->i_url; i++ )
1002     {
1003         msg_Err( host, "url still registered:%s", host->url[i]->psz_url );
1004     }
1005     for( i = 0; i < host->i_client; i++ )
1006     {
1007         httpd_client_t *cl = host->client[i];
1008         msg_Warn( host, "client still connected" );
1009         SOCKET_CLOSE( cl->fd );
1010         /* TODO */
1011     }
1012
1013     SOCKET_CLOSE( host->fd );
1014     vlc_mutex_destroy( &host->lock );
1015     vlc_object_destroy( host );
1016
1017     if( httpd->i_host <= 0 )
1018     {
1019         msg_Info( httpd, "httpd doesn't reference any host, deleting" );
1020         vlc_object_detach( httpd );
1021         vlc_object_destroy( httpd );
1022     }
1023     vlc_mutex_unlock( lockval.p_address );
1024 }
1025
1026 /* register a new url */
1027 static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password, vlc_bool_t b_check )
1028 {
1029     httpd_url_t *url;
1030     int         i;
1031
1032     vlc_mutex_lock( &host->lock );
1033     if( b_check )
1034     {
1035         for( i = 0; i < host->i_url; i++ )
1036         {
1037             if( !strcmp( psz_url, host->url[i]->psz_url ) )
1038             {
1039                 msg_Warn( host->httpd,
1040                           "cannot add '%s' (url already defined)", psz_url );
1041                 vlc_mutex_unlock( &host->lock );
1042                 return NULL;
1043             }
1044         }
1045     }
1046
1047     url = malloc( sizeof( httpd_url_t ) );
1048     url->host = host;
1049
1050     vlc_mutex_init( host->httpd, &url->lock );
1051     url->psz_url = strdup( psz_url );
1052     url->psz_user = strdup( psz_user ? psz_user : "" );
1053     url->psz_password = strdup( psz_password ? psz_password : "" );
1054     for( i = 0; i < HTTPD_MSG_MAX; i++ )
1055     {
1056         url->catch[i].cb = NULL;
1057         url->catch[i].p_sys = NULL;
1058     }
1059
1060     TAB_APPEND( host->i_url, host->url, url );
1061     vlc_mutex_unlock( &host->lock );
1062
1063     return url;
1064 }
1065
1066 httpd_url_t *httpd_UrlNew( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password )
1067 {
1068     return httpd_UrlNewPrivate( host, psz_url, psz_user, psz_password, VLC_FALSE );
1069 }
1070
1071 httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, char *psz_url, char *psz_user, char *psz_password )
1072 {
1073     return httpd_UrlNewPrivate( host, psz_url, psz_user, psz_password, VLC_TRUE );
1074 }
1075
1076 /* register callback on a url */
1077 int          httpd_UrlCatch( httpd_url_t *url, int i_msg,
1078                              httpd_callback_t cb,
1079                              httpd_callback_sys_t *p_sys )
1080 {
1081     vlc_mutex_lock( &url->lock );
1082     url->catch[i_msg].cb   = cb;
1083     url->catch[i_msg].p_sys= p_sys;
1084     vlc_mutex_unlock( &url->lock );
1085
1086     return VLC_SUCCESS;
1087 }
1088
1089
1090 /* delete an url */
1091 void         httpd_UrlDelete( httpd_url_t *url )
1092 {
1093     httpd_host_t *host = url->host;
1094     int          i;
1095
1096     vlc_mutex_lock( &host->lock );
1097     TAB_REMOVE( host->i_url, host->url, url );
1098
1099     vlc_mutex_destroy( &url->lock );
1100     free( url->psz_url );
1101     free( url->psz_user );
1102     free( url->psz_password );
1103
1104     for( i = 0; i < host->i_client; i++ )
1105     {
1106         httpd_client_t *client = host->client[i];
1107
1108         if( client->url == url )
1109         {
1110             /* TODO complete it */
1111             msg_Warn( host, "force closing connections" );
1112             SOCKET_CLOSE( client->fd );
1113             TAB_REMOVE( host->i_client, host->client, client );
1114             i--;
1115         }
1116     }
1117     vlc_mutex_unlock( &host->lock );
1118 }
1119
1120 void httpd_MsgInit( httpd_message_t *msg )
1121 {
1122     msg->cl         = NULL;
1123     msg->i_type     = HTTPD_MSG_NONE;
1124     msg->i_proto    = HTTPD_PROTO_NONE;
1125     msg->i_version  = -1;
1126
1127     msg->i_status   = 0;
1128     msg->psz_status = NULL;
1129
1130     msg->psz_url = NULL;
1131     msg->psz_args = NULL;
1132
1133     msg->i_channel = -1;
1134
1135     msg->i_name = 0;
1136     msg->name   = NULL;
1137     msg->i_value= 0;
1138     msg->value  = NULL;
1139
1140     msg->i_body_offset = 0;
1141     msg->i_body        = 0;
1142     msg->p_body        = 0;
1143 }
1144
1145 void httpd_MsgClean( httpd_message_t *msg )
1146 {
1147     int i;
1148
1149     if( msg->psz_status )
1150     {
1151         free( msg->psz_status );
1152     }
1153     if( msg->psz_url )
1154     {
1155         free( msg->psz_url );
1156     }
1157     if( msg->psz_args )
1158     {
1159         free( msg->psz_args );
1160     }
1161     for( i = 0; i < msg->i_name; i++ )
1162     {
1163         free( msg->name[i] );
1164         free( msg->value[i] );
1165     }
1166     if( msg->name )
1167     {
1168         free( msg->name );
1169     }
1170     if( msg->value )
1171     {
1172         free( msg->value );
1173     }
1174     if( msg->p_body )
1175     {
1176         free( msg->p_body );
1177     }
1178     httpd_MsgInit( msg );
1179 }
1180
1181 char *httpd_MsgGet( httpd_message_t *msg, char *name )
1182 {
1183     int i;
1184
1185     for( i = 0; i < msg->i_name; i++ )
1186     {
1187         if( !strcasecmp( msg->name[i], name ))
1188         {
1189             return msg->value[i];
1190         }
1191     }
1192     return "";
1193 }
1194 void httpd_MsgAdd( httpd_message_t *msg, char *name, char *psz_value, ... )
1195 {
1196     va_list args;
1197     char *value = NULL;
1198
1199     va_start( args, psz_value );
1200 #if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN) && !defined(SYS_BEOS)
1201     vasprintf( &value, psz_value, args );
1202 #else
1203     {
1204         int i_size = strlen( psz_value ) + 4096;    /* FIXME stupid system */
1205         value = calloc( i_size, sizeof( char ) );
1206         vsnprintf( value, i_size, psz_value, args );
1207         value[i_size - 1] = 0;
1208     }
1209 #endif
1210     va_end( args );
1211
1212     name = strdup( name );
1213
1214     TAB_APPEND( msg->i_name,  msg->name,  name );
1215     TAB_APPEND( msg->i_value, msg->value, value );
1216 }
1217
1218 static void httpd_ClientInit( httpd_client_t *cl )
1219 {
1220     cl->i_state = HTTPD_CLIENT_RECEIVING;
1221     cl->i_activity_date = mdate();
1222     cl->i_activity_timeout = 1500000000LL;
1223     cl->i_buffer_size = 10000;
1224     cl->i_buffer = 0;
1225     cl->p_buffer = malloc( cl->i_buffer_size );
1226     cl->i_mode   = HTTPD_CLIENT_FILE;
1227     cl->b_read_waiting = VLC_FALSE;
1228
1229     httpd_MsgInit( &cl->query );
1230     httpd_MsgInit( &cl->answer );
1231 }
1232 void httpd_ClientModeStream( httpd_client_t *cl )
1233 {
1234     cl->i_mode   = HTTPD_CLIENT_STREAM;
1235 }
1236 void httpd_ClientModeBidir( httpd_client_t *cl )
1237 {
1238     cl->i_mode   = HTTPD_CLIENT_BIDIR;
1239 }
1240
1241 static void httpd_ClientClean( httpd_client_t *cl )
1242 {
1243     if( cl->fd > 0 )
1244     {
1245         SOCKET_CLOSE( cl->fd );
1246     }
1247
1248     httpd_MsgClean( &cl->answer );
1249     httpd_MsgClean( &cl->query );
1250
1251     if( cl->p_buffer )
1252     {
1253         free( cl->p_buffer );
1254     }
1255 }
1256
1257 static httpd_client_t *httpd_ClientNew( int fd, struct sockaddr_in *sock )
1258 {
1259     httpd_client_t *cl = malloc( sizeof( httpd_client_t ) );
1260     /* set this new socket non-block */
1261 #if defined( WIN32 ) || defined( UNDER_CE )
1262     {
1263         unsigned long i_dummy = 1;
1264         ioctlsocket( fd, FIONBIO, &i_dummy );
1265     }
1266 #else
1267     fcntl( fd, F_SETFL, O_NONBLOCK );
1268 #endif
1269     cl->i_ref   = 0;
1270     cl->fd      = fd;
1271     cl->sock    = *sock;
1272     cl->url     = NULL;
1273
1274     httpd_ClientInit( cl );
1275
1276     return cl;
1277 }
1278
1279 static void httpd_ClientRecv( httpd_client_t *cl )
1280 {
1281     int i_len;
1282
1283     if( cl->query.i_proto == HTTPD_PROTO_NONE )
1284     {
1285         /* enought to see if it's rtp over rtsp or RTSP/HTTP */
1286         i_len = recv( cl->fd, &cl->p_buffer[cl->i_buffer], 4 - cl->i_buffer, 0 );
1287
1288         if( i_len > 0 )
1289         {
1290             cl->i_buffer += i_len;
1291         }
1292
1293         if( cl->i_buffer >= 4 )
1294         {
1295             fprintf( stderr, "peek=%4.4s\n", cl->p_buffer );
1296             /* detect type */
1297             if( cl->p_buffer[0] == '$' )
1298             {
1299                 /* RTSP (rtp over rtsp) */
1300                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1301                 cl->query.i_type  = HTTPD_MSG_CHANNEL;
1302                 cl->query.i_channel = cl->p_buffer[1];
1303                 cl->query.i_body  = (cl->p_buffer[2] << 8)|cl->p_buffer[3];
1304                 cl->query.p_body  = malloc( cl->query.i_body );
1305
1306                 cl->i_buffer      = 0;
1307             }
1308             else if( !strncmp( cl->p_buffer, "HTTP", 4 ) )
1309             {
1310                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1311                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1312             }
1313             else if( !strncmp( cl->p_buffer, "RTSP", 4 ) )
1314             {
1315                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1316                 cl->query.i_type  = HTTPD_MSG_ANSWER;
1317             }
1318             else if( !strncmp( cl->p_buffer, "GET", 3 ) || !strncmp( cl->p_buffer, "HEAD", 4 ) || !strncmp( cl->p_buffer, "POST", 4 ) )
1319             {
1320                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1321                 cl->query.i_type  = HTTPD_MSG_NONE;
1322             }
1323             else
1324             {
1325                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1326                 cl->query.i_type  = HTTPD_MSG_NONE;
1327             }
1328         }
1329     }
1330     else if( cl->query.i_body > 0 )
1331     {
1332         /* we are reading the body of a request or a channel */
1333         i_len = recv( cl->fd, &cl->query.p_body[cl->i_buffer], cl->query.i_body - cl->i_buffer, 0 );
1334         if( i_len > 0 )
1335         {
1336             cl->i_buffer += i_len;
1337         }
1338         if( cl->i_buffer >= cl->query.i_body )
1339         {
1340             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1341         }
1342     }
1343     else
1344     {
1345         /* we are reading a header -> char by char */
1346         for( ;; )
1347         {
1348             i_len = recv( cl->fd, &cl->p_buffer[cl->i_buffer], 1, 0 );
1349             if( i_len <= 0 )
1350             {
1351                 break;
1352             }
1353             cl->i_buffer++;
1354
1355             if( cl->i_buffer + 1 >= cl->i_buffer_size )
1356             {
1357                 cl->i_buffer_size += 1024;
1358                 cl->p_buffer = realloc( cl->p_buffer, cl->i_buffer_size );
1359             }
1360             if( ( cl->i_buffer >= 2 && !strncmp( &cl->p_buffer[cl->i_buffer-2], "\n\n", 2 ) )||
1361                 ( cl->i_buffer >= 4 && !strncmp( &cl->p_buffer[cl->i_buffer-4], "\r\n\r\n", 4 ) ) )
1362             {
1363                 char *p;
1364
1365                 /* we have finished the header so parse it and set i_body */
1366                 cl->p_buffer[cl->i_buffer] = '\0';
1367
1368                 if( cl->query.i_type == HTTPD_MSG_ANSWER )
1369                 {
1370                     cl->query.i_status = strtol( &cl->p_buffer[strlen( "HTTP/1.x" )], &p, 0 );
1371                     while( *p == ' ' )
1372                     {
1373                         p++;
1374                     }
1375                     cl->query.psz_status = strdup( p );
1376                 }
1377                 else
1378                 {
1379                     static const struct
1380                     {
1381                         char *name;
1382                         int  i_type;
1383                         int  i_proto;
1384                     }
1385                     msg_type[] =
1386                     {
1387                         { "GET",        HTTPD_MSG_GET,  HTTPD_PROTO_HTTP },
1388                         { "HEAD",       HTTPD_MSG_HEAD, HTTPD_PROTO_HTTP },
1389                         { "POST",       HTTPD_MSG_POST, HTTPD_PROTO_HTTP },
1390
1391                         { "OPTIONS",    HTTPD_MSG_OPTIONS,  HTTPD_PROTO_RTSP },
1392                         { "DESCRIBE",   HTTPD_MSG_DESCRIBE, HTTPD_PROTO_RTSP },
1393                         { "SETUP",      HTTPD_MSG_SETUP,    HTTPD_PROTO_RTSP },
1394                         { "PLAY",       HTTPD_MSG_PLAY,     HTTPD_PROTO_RTSP },
1395                         { "PAUSE",      HTTPD_MSG_PAUSE,    HTTPD_PROTO_RTSP },
1396                         { "TEARDOWN",   HTTPD_MSG_TEARDOWN, HTTPD_PROTO_RTSP },
1397
1398                         { NULL,         HTTPD_MSG_NONE,     HTTPD_PROTO_NONE }
1399                     };
1400                     int  i;
1401
1402                     p = NULL;
1403                     cl->query.i_type = HTTPD_MSG_NONE;
1404
1405                     fprintf( stderr, "received new request=%s\n", cl->p_buffer);
1406
1407                     for( i = 0; msg_type[i].name != NULL; i++ )
1408                     {
1409                         if( !strncmp( cl->p_buffer, msg_type[i].name, strlen( msg_type[i].name ) ) )
1410                         {
1411                             p = &cl->p_buffer[strlen(msg_type[i].name) + 1 ];
1412                             cl->query.i_type = msg_type[i].i_type;
1413                             if( cl->query.i_proto != msg_type[i].i_proto )
1414                             {
1415                                 p = NULL;
1416                                 cl->query.i_proto = HTTPD_PROTO_NONE;
1417                                 cl->query.i_type = HTTPD_MSG_NONE;
1418                             }
1419                             break;
1420                         }
1421                     }
1422                     if( p == NULL )
1423                     {
1424                         if( strstr( cl->p_buffer, "HTTP/1." ) )
1425                         {
1426                             cl->query.i_proto = HTTPD_PROTO_HTTP;
1427                         }
1428                         else if( strstr( cl->p_buffer, "RTSP/1." ) )
1429                         {
1430                             cl->query.i_proto = HTTPD_PROTO_RTSP;
1431                         }
1432                     }
1433                     else
1434                     {
1435                         char *p2;
1436                         char *p3;
1437
1438                         while( *p == ' ' )
1439                         {
1440                             p++;
1441                         }
1442                         p2 = strchr( p, ' ' );
1443                         if( p2 )
1444                         {
1445                             *p2++ = '\0';
1446                         }
1447                         if( !strncasecmp( p, "rtsp:", 5 ) )
1448                         {
1449                             /* for rtsp url, you have rtsp://localhost:port/path */
1450                             p += 5;
1451                             while( *p == '/' ) p++;
1452                             while( *p && *p != '/' ) p++;
1453                         }
1454                         cl->query.psz_url = strdup( p );
1455                         if( ( p3 = strchr( cl->query.psz_url, '?' ) )  )
1456                         {
1457                             *p3++ = '\0';
1458                             cl->query.psz_args = strdup( p3 );
1459                         }
1460                         if( p2 )
1461                         {
1462                             while( *p2 == ' ' )
1463                             {
1464                                 p2++;
1465                             }
1466                             if( !strncasecmp( p2, "HTTP/1.", 7 ) )
1467                             {
1468                                 cl->query.i_proto = HTTPD_PROTO_HTTP;
1469                                 cl->query.i_version = atoi( p2+7 );
1470                             }
1471                             else if( !strncasecmp( p2, "RTSP/1.", 7 ) )
1472                             {
1473                                 cl->query.i_proto = HTTPD_PROTO_RTSP;
1474                                 cl->query.i_version = atoi( p2+7 );
1475                             }
1476                         }
1477                         p = p2;
1478                     }
1479                 }
1480                 if( p )
1481                 {
1482                     p = strchr( p, '\n' );
1483                 }
1484                 if( p )
1485                 {
1486                     while( *p == '\n' || *p == '\r' )
1487                     {
1488                         p++;
1489                     }
1490                     while( p && *p != '\0' )
1491                     {
1492                         char *line = p;
1493                         char *eol = p = strchr( p, '\n' );
1494                         char *colon;
1495
1496                         while( eol && eol >= line && ( *eol == '\n' || *eol == '\r' ) )
1497                         {
1498                             *eol-- = '\0';
1499                         }
1500
1501                         if( ( colon = strchr( line, ':' ) ) )
1502                         {
1503                             char *name;
1504                             char *value;
1505
1506                             *colon++ = '\0';
1507                             while( *colon == ' ' )
1508                             {
1509                                 colon++;
1510                             }
1511                             name = strdup( line );
1512                             value = strdup( colon );
1513
1514                             TAB_APPEND( cl->query.i_name, cl->query.name, name );
1515                             TAB_APPEND( cl->query.i_value,cl->query.value,value);
1516
1517                             if( !strcasecmp( name, "Content-Length" ) )
1518                             {
1519                                 cl->query.i_body = atol( value );
1520                             }
1521                         }
1522
1523                         if( p )
1524                         {
1525                             p++;
1526                             while( *p == '\n' || *p == '\r' )
1527                             {
1528                                 p++;
1529                             }
1530                         }
1531                     }
1532                 }
1533                 if( cl->query.i_body > 0 )
1534                 {
1535                     /* TODO Mhh, handle the case client will only send a request and close the connection
1536                      * to mark and of body (probably only RTSP) */
1537                     cl->query.p_body = malloc( cl->query.i_body );
1538                     cl->i_buffer = 0;
1539                 }
1540                 else
1541                 {
1542                     cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1543                 }
1544             }
1545         }
1546     }
1547
1548     /* check if the client is to be set to dead */
1549 #if defined( WIN32 ) || defined( UNDER_CE )
1550     if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1551 #else
1552     if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1553 #endif
1554     {
1555         if( cl->query.i_proto != HTTPD_PROTO_NONE && cl->query.i_type != HTTPD_MSG_NONE )
1556         {
1557             /* connection closed -> end of data */
1558             if( cl->query.i_body > 0 )
1559             {
1560                 cl->query.i_body = cl->i_buffer;
1561             }
1562             cl->i_state = HTTPD_CLIENT_RECEIVE_DONE;
1563         }
1564         else
1565         {
1566             cl->i_state = HTTPD_CLIENT_DEAD;
1567         }
1568     }
1569     cl->i_activity_date = mdate();
1570
1571     /* Debugging only */
1572     if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1573     {
1574         int i;
1575
1576         fprintf( stderr, "received new request\n" );
1577         fprintf( stderr, "  - proto=%s\n", cl->query.i_proto == HTTPD_PROTO_HTTP ? "HTTP" : "RTSP" );
1578         fprintf( stderr, "  - version=%d\n", cl->query.i_version );
1579         fprintf( stderr, "  - msg=%d\n", cl->query.i_type );
1580         if( cl->query.i_type == HTTPD_MSG_ANSWER )
1581         {
1582             fprintf( stderr, "  - answer=%d '%s'\n", cl->query.i_status, cl->query.psz_status );
1583         }
1584         else if( cl->query.i_type != HTTPD_MSG_NONE )
1585         {
1586             fprintf( stderr, "  - url=%s\n", cl->query.psz_url );
1587         }
1588         for( i = 0; i < cl->query.i_name; i++ )
1589         {
1590             fprintf( stderr, "  - option name='%s' value='%s'\n", cl->query.name[i], cl->query.value[i] );
1591         }
1592     }
1593 }
1594
1595 static void httpd_ClientSend( httpd_client_t *cl )
1596 {
1597     int i;
1598     int i_len;
1599
1600     if( cl->i_buffer < 0 )
1601     {
1602         /* We need to create the header */
1603         int i_size = 0;
1604         char *p;
1605
1606         i_size = strlen( "HTTP/1.") + 10 + 10 +
1607                  strlen( cl->answer.psz_status ? cl->answer.psz_status : "" ) + 5;
1608         for( i = 0; i < cl->answer.i_name; i++ )
1609         {
1610             i_size += strlen( cl->answer.name[i] ) + 2 + strlen( cl->answer.value[i] ) + 2;
1611         }
1612
1613         if( cl->i_buffer_size < i_size )
1614         {
1615             cl->i_buffer_size = i_size;
1616             free( cl->p_buffer );
1617             cl->p_buffer = malloc( i_size );
1618         }
1619         p = cl->p_buffer;
1620
1621         p += sprintf( p, "%s/1.%d %d %s\r\n",
1622                       cl->answer.i_proto ==  HTTPD_PROTO_HTTP ? "HTTP" : "RTSP",
1623                       cl->answer.i_version,
1624                       cl->answer.i_status, cl->answer.psz_status );
1625         for( i = 0; i < cl->answer.i_name; i++ )
1626         {
1627             p += sprintf( p, "%s: %s\r\n", cl->answer.name[i], cl->answer.value[i] );
1628         }
1629         p += sprintf( p, "\r\n" );
1630
1631         cl->i_buffer = 0;
1632         cl->i_buffer_size = (uint8_t*)p - cl->p_buffer;
1633
1634         fprintf( stderr, "sending answer\n" );
1635         fprintf( stderr, "%s",  cl->p_buffer );
1636     }
1637
1638     i_len = send( cl->fd, &cl->p_buffer[cl->i_buffer], cl->i_buffer_size - cl->i_buffer, 0 );
1639     if( i_len > 0 )
1640     {
1641         cl->i_activity_date = mdate();
1642         cl->i_buffer += i_len;
1643
1644         if( cl->i_buffer >= cl->i_buffer_size )
1645         {
1646             if( cl->answer.i_body == 0  && cl->answer.i_body_offset > 0 && !cl->b_read_waiting )
1647             {
1648                 /* catch more body data */
1649                 int i_msg = cl->query.i_type;
1650                 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl,
1651                                           &cl->answer, &cl->query );
1652             }
1653
1654             if( cl->answer.i_body > 0 )
1655             {
1656                 /* send the body data */
1657                 free( cl->p_buffer );
1658                 cl->p_buffer = cl->answer.p_body;
1659                 cl->i_buffer_size = cl->answer.i_body;
1660                 cl->i_buffer = 0;
1661
1662                 cl->answer.i_body = 0;
1663                 cl->answer.p_body = NULL;
1664             }
1665             else
1666             {
1667                 /* send finished */
1668                 cl->i_state = HTTPD_CLIENT_SEND_DONE;
1669             }
1670         }
1671     }
1672     else
1673     {
1674 #if defined( WIN32 ) || defined( UNDER_CE )
1675         if( ( i_len < 0 && WSAGetLastError() != WSAEWOULDBLOCK ) || ( i_len == 0 ) )
1676 #else
1677         if( ( i_len < 0 && errno != EAGAIN && errno != EINTR ) || ( i_len == 0 ) )
1678 #endif
1679         {
1680             /* error */
1681             cl->i_state = HTTPD_CLIENT_DEAD;
1682         }
1683     }
1684 }
1685
1686 static void httpd_HostThread( httpd_host_t *host )
1687 {
1688     while( !host->b_die )
1689     {
1690         struct timeval  timeout;
1691         fd_set          fds_read;
1692         fd_set          fds_write;
1693         int             i_handle_max = 0;
1694         int             i_ret;
1695         int             i_client;
1696         int             b_low_delay = 0;
1697
1698         if( host->i_url <= 0 )
1699         {
1700             /* 0.2s */
1701             msleep( 200000 );
1702             continue;
1703         }
1704
1705         /* built a set of handle to select */
1706         FD_ZERO( &fds_read );
1707         FD_ZERO( &fds_write );
1708
1709         FD_SET( host->fd, &fds_read );
1710         i_handle_max = host->fd;
1711
1712         /* add all socket that should be read/write and close dead connection */
1713         vlc_mutex_lock( &host->lock );
1714         for( i_client = 0; i_client < host->i_client; i_client++ )
1715         {
1716             httpd_client_t *cl = host->client[i_client];
1717
1718             if( cl->i_ref < 0 ||
1719                 ( cl->i_ref == 0 &&
1720                     ( cl->i_state == HTTPD_CLIENT_DEAD ||
1721                       cl->i_activity_date + cl->i_activity_timeout < mdate() ) ) )
1722             {
1723                 msg_Dbg( host, "connection closed(%s)", inet_ntoa(cl->sock.sin_addr) );
1724
1725                 httpd_ClientClean( cl );
1726
1727                 TAB_REMOVE( host->i_client, host->client, cl );
1728                 i_client--;
1729             }
1730             else if( cl->i_state == HTTPD_CLIENT_RECEIVING )
1731             {
1732                 FD_SET( cl->fd, &fds_read );
1733                 i_handle_max = __MAX( i_handle_max, cl->fd );
1734             }
1735             else if( cl->i_state == HTTPD_CLIENT_SENDING )
1736             {
1737                 FD_SET( cl->fd, &fds_write );
1738                 i_handle_max = __MAX( i_handle_max, cl->fd );
1739             }
1740             else if( cl->i_state == HTTPD_CLIENT_RECEIVE_DONE )
1741             {
1742                 httpd_message_t *answer = &cl->answer;
1743                 httpd_message_t *query  = &cl->query;
1744                 int i_msg = query->i_type;
1745
1746                 httpd_MsgInit( answer );
1747
1748                 /* Handle what we received */
1749                 if( cl->i_mode != HTTPD_CLIENT_BIDIR && ( i_msg == HTTPD_MSG_ANSWER || i_msg == HTTPD_MSG_CHANNEL ) )
1750                 {
1751                     /* we can only receive request from client when not in BIDIR mode */
1752                     cl->url     = NULL;
1753                     cl->i_state = HTTPD_CLIENT_DEAD;
1754                 }
1755                 else if( i_msg == HTTPD_MSG_ANSWER )
1756                 {
1757                     /* We are in BIDIR mode, trigger the callback and then check for new data */
1758                     if( cl->url && cl->url->catch[i_msg].cb )
1759                     {
1760                         cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, NULL, query );
1761                     }
1762                     cl->i_state = HTTPD_CLIENT_WAITING;
1763                 }
1764                 else if( i_msg == HTTPD_MSG_CHANNEL )
1765                 {
1766                     /* We are in BIDIR mode, trigger the callback and then check for new data */
1767                     if( cl->url && cl->url->catch[i_msg].cb )
1768                     {
1769                         cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, NULL, query );
1770                     }
1771                     cl->i_state = HTTPD_CLIENT_WAITING;
1772                 }
1773                 else if( i_msg == HTTPD_MSG_OPTIONS )
1774                 {
1775                     int i_cseq;
1776
1777                     /* unimplemented */
1778                     answer->i_proto  = query->i_proto ;
1779                     answer->i_type   = HTTPD_MSG_ANSWER;
1780                     answer->i_version= 0;
1781                     answer->i_status = 200;
1782                     answer->psz_status = strdup( "Ok" );
1783
1784                     answer->i_body = 0;
1785                     answer->p_body = NULL;
1786
1787                     i_cseq = atoi( httpd_MsgGet( query, "Cseq" ) );
1788                     httpd_MsgAdd( answer, "Cseq", "%d", i_cseq );
1789                     httpd_MsgAdd( answer, "Server", "VLC Server" );
1790                     httpd_MsgAdd( answer, "Public", "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE" );
1791                     httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
1792
1793                     cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
1794                     cl->i_state = HTTPD_CLIENT_SENDING;
1795                 }
1796                 else if( i_msg == HTTPD_MSG_NONE )
1797                 {
1798                     if( query->i_proto == HTTPD_PROTO_NONE )
1799                     {
1800                         cl->url = NULL;
1801                         cl->i_state = HTTPD_CLIENT_DEAD;
1802                     }
1803                     else
1804                     {
1805                         uint8_t *p;
1806
1807                         /* unimplemented */
1808                         answer->i_proto  = query->i_proto ;
1809                         answer->i_type   = HTTPD_MSG_ANSWER;
1810                         answer->i_version= 0;
1811                         answer->i_status = 501;
1812                         answer->psz_status = strdup( "Unimplemented" );
1813
1814                         p = answer->p_body = malloc( 1000 );
1815
1816                         p += sprintf( p, "<html>\n" );
1817                         p += sprintf( p, "<head>\n" );
1818                         p += sprintf( p, "<title>Error 501</title>\n" );
1819                         p += sprintf( p, "</head>\n" );
1820                         p += sprintf( p, "<body>\n" );
1821                         p += sprintf( p, "<h1><center> 501 Unimplemented</center></h1>\n" );
1822                         p += sprintf( p, "<hr />\n" );
1823                         p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1824                         p += sprintf( p, "</body>\n" );
1825                         p += sprintf( p, "</html>\n" );
1826
1827                         answer->i_body = p - answer->p_body;
1828                         httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
1829
1830                         cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
1831                         cl->i_state = HTTPD_CLIENT_SENDING;
1832                     }
1833                 }
1834                 else
1835                 {
1836                     vlc_bool_t b_auth_failed = VLC_FALSE;
1837                     int i;
1838
1839                     /* Search the url and trigger callbacks */
1840                     for( i = 0; i < host->i_url; i++ )
1841                     {
1842                         httpd_url_t *url = host->url[i];
1843
1844                         if( !strcmp( url->psz_url, query->psz_url ) )
1845                         {
1846                             if( url->catch[i_msg].cb )
1847                             {
1848                                 if( answer && ( *url->psz_user || *url->psz_password ) )
1849                                 {
1850                                     /* create the headers */
1851                                     char id[strlen(url->psz_user)+strlen(url->psz_password) + 2];
1852                                     char *b64 = httpd_MsgGet( query, "Authorization" ); /* BASIC id */
1853                                     char auth[strlen(b64) +1];
1854
1855                                     sprintf( id, "%s:%s", url->psz_user, url->psz_password );
1856                                     if( !strncasecmp( b64, "BASIC", 5 ) )
1857                                     {
1858                                         b64 += 5;
1859                                         while( *b64 == ' ' )
1860                                         {
1861                                             b64++;
1862                                         }
1863                                         b64_decode( auth, b64 );
1864                                     }
1865                                     else
1866                                     {
1867                                         strcpy( auth, "" );
1868                                     }
1869                                     if( strcmp( id, auth ) )
1870                                     {
1871                                         httpd_MsgAdd( answer, "WWW-Authenticate", "Basic realm=\"%s\"", url->psz_user );
1872                                         /* We fail for all url */
1873                                         b_auth_failed = VLC_TRUE;
1874                                         break;
1875                                     }
1876                                 }
1877
1878                                 if( !url->catch[i_msg].cb( url->catch[i_msg].p_sys, cl, answer, query ) )
1879                                 {
1880                                     /* only one url can answer */
1881                                     answer = NULL;
1882                                     if( cl->url == NULL )
1883                                     {
1884                                         cl->url = url;
1885                                     }
1886                                 }
1887                             }
1888                         }
1889                     }
1890                     if( answer )
1891                     {
1892                         uint8_t *p;
1893
1894                         answer->i_proto  = query->i_proto;
1895                         answer->i_type   = HTTPD_MSG_ANSWER;
1896                         answer->i_version= 0;
1897                         p = answer->p_body = malloc( 1000 + strlen(query->psz_url) );
1898
1899                         if( b_auth_failed )
1900                         {
1901                             answer->i_status = 401;
1902                             answer->psz_status = strdup( "Authorization Required" );
1903
1904                             p += sprintf( p, "<html>\n" );
1905                             p += sprintf( p, "<head>\n" );
1906                             p += sprintf( p, "<title>Error 401</title>\n" );
1907                             p += sprintf( p, "</head>\n" );
1908                             p += sprintf( p, "<body>\n" );
1909                             p += sprintf( p, "<h1><center> 401 Authorization Required (%s)</center></h1>\n", query->psz_url );
1910                             p += sprintf( p, "<hr />\n" );
1911                             p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1912                             p += sprintf( p, "</body>\n" );
1913                             p += sprintf( p, "</html>\n" );
1914                         }
1915                         else
1916                         {
1917                             /* no url registered */
1918                             answer->i_status = 404;
1919                             answer->psz_status = strdup( "Not found" );
1920
1921                             p += sprintf( p, "<html>\n" );
1922                             p += sprintf( p, "<head>\n" );
1923                             p += sprintf( p, "<title>Error 404</title>\n" );
1924                             p += sprintf( p, "</head>\n" );
1925                             p += sprintf( p, "<body>\n" );
1926                             p += sprintf( p, "<h1><center> 404 Ressource not found(%s)</center></h1>\n", query->psz_url );
1927                             p += sprintf( p, "<hr />\n" );
1928                             p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1929                             p += sprintf( p, "</body>\n" );
1930                             p += sprintf( p, "</html>\n" );
1931                         }
1932
1933                         answer->i_body = p - answer->p_body;
1934                         httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );
1935                     }
1936                     cl->i_buffer = -1;  /* Force the creation of the answer in httpd_ClientSend */
1937                     cl->i_state = HTTPD_CLIENT_SENDING;
1938                 }
1939             }
1940             else if( cl->i_state == HTTPD_CLIENT_SEND_DONE )
1941             {
1942                 if( cl->i_mode == HTTPD_CLIENT_FILE )
1943                 {
1944                     cl->url = NULL;
1945                     if( ( cl->query.i_proto == HTTPD_PROTO_HTTP &&
1946                           ( !strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Keep-Alive" )||
1947                           ( cl->answer.i_version == 1 && strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Close" ) ) ) ) ||
1948                         ( cl->query.i_proto == HTTPD_PROTO_RTSP &&
1949                           strcasecmp( httpd_MsgGet( &cl->query, "Connection" ), "Close" ) &&
1950                           strcasecmp( httpd_MsgGet( &cl->answer, "Connection" ), "Close" ) ) )
1951                     {
1952                         httpd_MsgClean( &cl->query );
1953                         httpd_MsgInit( &cl->query );
1954
1955                         cl->i_buffer = 0;
1956                         cl->i_buffer_size = 1000;
1957                         free( cl->p_buffer );
1958                         cl->p_buffer = malloc( cl->i_buffer_size );
1959                         cl->i_state = HTTPD_CLIENT_RECEIVING;
1960                     }
1961                     else
1962                     {
1963                         cl->i_state = HTTPD_CLIENT_DEAD;
1964                     }
1965                     httpd_MsgClean( &cl->answer );
1966                 }
1967                 else if( cl->b_read_waiting )
1968                 {
1969                     /* we have a message waiting for us to read it */
1970                     httpd_MsgClean( &cl->answer );
1971                     httpd_MsgClean( &cl->query );
1972
1973                     cl->i_buffer = 0;
1974                     cl->i_buffer_size = 1000;
1975                     free( cl->p_buffer );
1976                     cl->p_buffer = malloc( cl->i_buffer_size );
1977                     cl->i_state = HTTPD_CLIENT_RECEIVING;
1978                     cl->b_read_waiting = VLC_FALSE;
1979                 }
1980                 else
1981                 {
1982                     int64_t i_offset = cl->answer.i_body_offset;
1983                     httpd_MsgClean( &cl->answer );
1984
1985                     cl->answer.i_body_offset = i_offset;
1986                     cl->i_state = HTTPD_CLIENT_WAITING;
1987                 }
1988             }
1989             else if( cl->i_state == HTTPD_CLIENT_WAITING )
1990             {
1991                 int64_t i_offset = cl->answer.i_body_offset;
1992                 int     i_msg = cl->query.i_type;
1993
1994                 httpd_MsgInit( &cl->answer );
1995                 cl->answer.i_body_offset = i_offset;
1996
1997                 cl->url->catch[i_msg].cb( cl->url->catch[i_msg].p_sys, cl, &cl->answer, &cl->query );
1998                 if( cl->answer.i_type != HTTPD_MSG_NONE )
1999                 {
2000                     /* we have new data, so reenter send mode */
2001                     cl->i_buffer      = 0;
2002                     cl->p_buffer      = cl->answer.p_body;
2003                     cl->i_buffer_size = cl->answer.i_body;
2004                     cl->answer.p_body = NULL;
2005                     cl->answer.i_body = 0;
2006                     cl->i_state = HTTPD_CLIENT_SENDING;
2007                 }
2008                 else
2009                 {
2010                     /* we shouldn't wait too long */
2011                     b_low_delay = VLC_TRUE;
2012                 }
2013             }
2014
2015             /* Special for BIDIR mode we also check reading */
2016             if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING )
2017             {
2018                 FD_SET( cl->fd, &fds_read );
2019                 i_handle_max = __MAX( i_handle_max, cl->fd );
2020             }
2021         }
2022         vlc_mutex_unlock( &host->lock );
2023
2024         /* we will wait 100ms or 20ms (not too big 'cause HTTPD_CLIENT_WAITING) */
2025         timeout.tv_sec = 0;
2026         timeout.tv_usec = b_low_delay ? 20000 : 100000;
2027
2028         i_ret = select( i_handle_max + 1,
2029                         &fds_read, &fds_write, NULL, &timeout );
2030
2031         if( i_ret == -1 && errno != EINTR )
2032         {
2033             msg_Warn( host, "cannot select sockets" );
2034             msleep( 1000 );
2035             continue;
2036         }
2037         else if( i_ret <= 0 )
2038         {
2039             continue;
2040         }
2041
2042         /* accept new connections */
2043         if( FD_ISSET( host->fd, &fds_read ) )
2044         {
2045             int     i_sock_size = sizeof( struct sockaddr_in );
2046             struct  sockaddr_in sock;
2047             int     fd;
2048
2049             fd = accept( host->fd, (struct sockaddr *)&sock, &i_sock_size );
2050             if( fd > 0 )
2051             {
2052                 httpd_client_t *cl = httpd_ClientNew( fd, &sock );
2053
2054                 vlc_mutex_lock( &host->lock );
2055                 TAB_APPEND( host->i_client, host->client, cl );
2056                 vlc_mutex_unlock( &host->lock );
2057
2058                 msg_Dbg( host, "new connection (%s)", inet_ntoa(sock.sin_addr) );
2059             }
2060         }
2061         /* now try all others socket */
2062         vlc_mutex_lock( &host->lock );
2063         for( i_client = 0; i_client < host->i_client; i_client++ )
2064         {
2065             httpd_client_t *cl = host->client[i_client];
2066             if( cl->i_state == HTTPD_CLIENT_RECEIVING )
2067             {
2068                 httpd_ClientRecv( cl );
2069             }
2070             else if( cl->i_state == HTTPD_CLIENT_SENDING )
2071             {
2072                 httpd_ClientSend( cl );
2073             }
2074
2075             if( cl->i_mode == HTTPD_CLIENT_BIDIR && cl->i_state == HTTPD_CLIENT_SENDING &&
2076                 FD_ISSET( cl->fd, &fds_read ) )
2077             {
2078                 cl->b_read_waiting = VLC_TRUE;
2079             }
2080         }
2081         vlc_mutex_unlock( &host->lock );
2082     }
2083 }
2084
2085
2086
2087
2088
2089 static int BuildAddr( struct sockaddr_in * p_socket,
2090                       const char * psz_address, int i_port )
2091 {
2092     /* Reset struct */
2093     memset( p_socket, 0, sizeof( struct sockaddr_in ) );
2094     p_socket->sin_family = AF_INET;                                /* family */
2095     p_socket->sin_port = htons( (uint16_t)i_port );
2096     if( !*psz_address )
2097     {
2098         p_socket->sin_addr.s_addr = INADDR_ANY;
2099     }
2100     else
2101     {
2102         struct hostent    * p_hostent;
2103
2104         /* Try to convert address directly from in_addr - this will work if
2105          * psz_address is dotted decimal. */
2106 #ifdef HAVE_ARPA_INET_H
2107         if( !inet_aton( psz_address, &p_socket->sin_addr ) )
2108 #else
2109         p_socket->sin_addr.s_addr = inet_addr( psz_address );
2110         if( p_socket->sin_addr.s_addr == INADDR_NONE )
2111 #endif
2112         {
2113             /* We have a fqdn, try to find its address */
2114             if ( (p_hostent = gethostbyname( psz_address )) == NULL )
2115             {
2116                 return( -1 );
2117             }
2118
2119             /* Copy the first address of the host in the socket address */
2120             memcpy( &p_socket->sin_addr, p_hostent->h_addr_list[0],
2121                      p_hostent->h_length );
2122         }
2123     }
2124     return( 0 );
2125 }