httpd.c 81.4 KB
Newer Older
1
2
3
/*****************************************************************************
 * httpd.c
 *****************************************************************************
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
4
 * Copyright (C) 2004-2006 the VideoLAN team
5
 * Copyright © 2004-2007 Rémi Denis-Courmont
6
 * $Id$
7
8
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9
 *          Rémi Denis-Courmont <rem # videolan.org>
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
25
 *****************************************************************************/

26
27
28
29
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30
#include <vlc/vlc.h>
Rafaël Carré's avatar
Rafaël Carré committed
31
#include <vlc_httpd.h>
32

33
34
#ifdef ENABLE_HTTPD

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
35
36
#include <assert.h>

zorglub's avatar
zorglub committed
37
38
39
#include <vlc_network.h>
#include <vlc_tls.h>
#include <vlc_acl.h>
40
#include <vlc_strings.h>
41
#include "../libvlc.h"
42

43
#include <string.h>
44
45
#include <errno.h>

46
47
48
#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif
gbazin's avatar
gbazin committed
49
50
51
52

#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif
53

54
55
56
57
#ifdef HAVE_POLL
# include <poll.h>
#endif

58
59
60
61
62
63
64
65
#if defined( UNDER_CE )
#   include <winsock.h>
#elif defined( WIN32 )
#   include <winsock2.h>
#else
#   include <sys/socket.h>
#endif

66
67
68
69
70
71
72
#if defined( WIN32 )
/* We need HUGE buffer otherwise TCP throughput is very limited */
#define HTTPD_CL_BUFSIZE 1000000
#else
#define HTTPD_CL_BUFSIZE 10000
#endif

73
74
static void httpd_ClientClean( httpd_client_t *cl );

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
struct httpd_t
{
    VLC_COMMON_MEMBERS

    int          i_host;
    httpd_host_t **host;
};


/* each host run in his own thread */
struct httpd_host_t
{
    VLC_COMMON_MEMBERS

    httpd_t     *httpd;

    /* ref count */
    int         i_ref;

    /* address/port and socket for listening at connections */
    char        *psz_hostname;
    int         i_port;
97
98
    int         *fds;
    unsigned     nfd;
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

    vlc_mutex_t lock;

    /* all registered url (becarefull that 2 httpd_url_t could point at the same url)
     * This will slow down the url research but make my live easier
     * All url will have their cb trigger, but only the first one can answer
     * */
    int         i_url;
    httpd_url_t **url;

    int            i_client;
    httpd_client_t **client;

    /* TLS data */
    tls_server_t *p_tls;
};


117
118
119
120
121
122
struct httpd_url_t
{
    httpd_host_t *host;

    vlc_mutex_t lock;

123
124
125
126
    char      *psz_url;
    char      *psz_user;
    char      *psz_password;
    vlc_acl_t *p_acl;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

    struct
    {
        httpd_callback_t     cb;
        httpd_callback_sys_t *p_sys;
    } catch[HTTPD_MSG_MAX];
};

/* status */
enum
{
    HTTPD_CLIENT_RECEIVING,
    HTTPD_CLIENT_RECEIVE_DONE,

    HTTPD_CLIENT_SENDING,
    HTTPD_CLIENT_SEND_DONE,

    HTTPD_CLIENT_WAITING,

    HTTPD_CLIENT_DEAD,
147
148
149

    HTTPD_CLIENT_TLS_HS_IN,
    HTTPD_CLIENT_TLS_HS_OUT
150
};
Jean-Paul Saman's avatar
Jean-Paul Saman committed
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* mode */
enum
{
    HTTPD_CLIENT_FILE,      /* default */
    HTTPD_CLIENT_STREAM,    /* regulary get data from cb */
    HTTPD_CLIENT_BIDIR,     /* check for reading and get data from cb */
};

struct httpd_client_t
{
    httpd_url_t *url;

    int     i_ref;

    int     fd;

    int     i_mode;
    int     i_state;
    int     b_read_waiting; /* stop as soon as possible sending */

    mtime_t i_activity_date;
    mtime_t i_activity_timeout;

    /* buffer for reading header */
    int     i_buffer_size;
    int     i_buffer;
    uint8_t *p_buffer;

    /* */
    httpd_message_t query;  /* client -> httpd */
    httpd_message_t answer; /* httpd -> client */
Jean-Paul Saman's avatar
Jean-Paul Saman committed
183

184
185
    /* TLS data */
    tls_session_t *p_tls;
186
187
188
189
190
191
192
193
};


/*****************************************************************************
 * Various functions
 *****************************************************************************/
static struct
{
194
    const char psz_ext[8];
195
    const char *psz_mime;
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
} http_mime[] =
{
    { ".htm",   "text/html" },
    { ".html",  "text/html" },
    { ".txt",   "text/plain" },
    { ".xml",   "text/xml" },
    { ".dtd",   "text/dtd" },

    { ".css",   "text/css" },

    /* image mime */
    { ".gif",   "image/gif" },
    { ".jpe",   "image/jpeg" },
    { ".jpg",   "image/jpeg" },
    { ".jpeg",  "image/jpeg" },
    { ".png",   "image/png" },
212
213
    /* same as modules/mux/mpjpeg.c here: */
    { ".mpjpeg","multipart/x-mixed-replace; boundary=7b3cc56e5f51db803f790dad720ed50a" },
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

    /* media mime */
    { ".avi",   "video/avi" },
    { ".asf",   "video/x-ms-asf" },
    { ".m1a",   "audio/mpeg" },
    { ".m2a",   "audio/mpeg" },
    { ".m1v",   "video/mpeg" },
    { ".m2v",   "video/mpeg" },
    { ".mp2",   "audio/mpeg" },
    { ".mp3",   "audio/mpeg" },
    { ".mpa",   "audio/mpeg" },
    { ".mpg",   "video/mpeg" },
    { ".mpeg",  "video/mpeg" },
    { ".mpe",   "video/mpeg" },
    { ".mov",   "video/quicktime" },
    { ".moov",  "video/quicktime" },
    { ".ogg",   "application/ogg" },
    { ".ogm",   "application/ogg" },
    { ".wav",   "audio/wav" },
    { ".wma",   "audio/x-ms-wma" },
    { ".wmv",   "video/x-ms-wmv" },


    /* end */
238
    { "",       "" }
239
};
Jean-Paul Saman's avatar
Jean-Paul Saman committed
240

241
static const char *httpd_MimeFromUrl( const char *psz_url )
242
243
244
245
246
247
248
249
250
{

    char *psz_ext;

    psz_ext = strrchr( psz_url, '.' );
    if( psz_ext )
    {
        int i;

251
        for( i = 0; http_mime[i].psz_ext[0] ; i++ )
252
253
254
255
256
257
258
259
260
261
        {
            if( !strcasecmp( http_mime[i].psz_ext, psz_ext ) )
            {
                return http_mime[i].psz_mime;
            }
        }
    }
    return "application/octet-stream";
}

262

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
263
264
typedef struct
{
265
266
    unsigned   i_code;
    const char psz_reason[36];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
267
268
269
270
271
272
} http_status_info;

static const http_status_info http_reason[] =
{
  /*{ 100, "Continue" },
    { 101, "Switching Protocols" },*/
273
274
    { 200, "OK" },
  /*{ 201, "Created" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
275
    { 202, "Accepted" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
276
277
278
279
280
281
    { 203, "Non-authoritative information" },
    { 204, "No content" },
    { 205, "Reset content" },
    { 206, "Partial content" },
    { 250, "Low on storage space" },
    { 300, "Multiple choices" },*/
282
283
    { 301, "Moved permanently" },
  /*{ 302, "Moved temporarily" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
284
285
286
287
288
    { 303, "See other" },
    { 304, "Not modified" },
    { 305, "Use proxy" },
    { 307, "Temporary redirect" },
    { 400, "Bad request" },*/
289
290
    { 401, "Unauthorized" },
  /*{ 402, "Payment Required" },*/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
291
    { 403, "Forbidden" },
292
    { 404, "Not found" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
293
294
295
296
    { 405, "Method not allowed" },
  /*{ 406, "Not acceptable" },
    { 407, "Proxy authentication required" },
    { 408, "Request time-out" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
297
298
    { 409, "Conflict" },
    { 410, "Gone" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
299
300
301
302
303
    { 411, "Length required" },
    { 412, "Precondition failed" },
    { 413, "Request entity too large" },
    { 414, "Request-URI too large" },
    { 415, "Unsupported media Type" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
304
    { 416, "Requested range not satisfiable" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
305
306
307
308
    { 417, "Expectation failed" },
    { 451, "Parameter not understood" },
    { 452, "Conference not found" },
    { 453, "Not enough bandwidth" },*/
309
    { 454, "Session not found" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
310
  /*{ 455, "Method not valid in this State" },*/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
311
    { 456, "Header field not valid for resource" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
312
  /*{ 457, "Invalid range" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
313
    { 458, "Read-only parameter" },*/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
314
    { 459, "Aggregate operation not allowed" },
315
316
317
    { 460, "Non-aggregate operation not allowed" },
    { 461, "Unsupported transport" },
  /*{ 462, "Destination unreachable" },*/
318
    { 500, "Internal server error" },
319
320
321
    { 501, "Not implemented" },
  /*{ 502, "Bad gateway" },*/
    { 503, "Service unavailable" },
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
322
323
  /*{ 504, "Gateway time-out" },*/
    { 505, "Protocol version not supported" },
324
325
    { 551, "Option not supported" },
    { 999, "" }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
326
327
};

328
329
static const char psz_fallback_reason[5][16] =
{ "Continue", "OK", "Found", "Client error", "Server error" };
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
330

331
static const char *httpd_ReasonFromCode( unsigned i_code )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
332
333
334
{
    const http_status_info *p;

335
336
337
    assert( ( i_code >= 100 ) && ( i_code <= 599 ) );

    for (p = http_reason; i_code > p->i_code; p++);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
338
339
340
341
342
343

    if( p->i_code == i_code )
        return p->psz_reason;

    return psz_fallback_reason[(i_code / 100) - 1];
}
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375


static size_t httpd_HtmlError (char **body, int code, const char *url)
{
    const char *errname = httpd_ReasonFromCode (code);
    assert (errname != NULL);

    int res = asprintf (body,
        "<?xml version=\"1.0\" encoding=\"ascii\" ?>\n"
        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
        " \"http://www.w3.org/TR/xhtml10/DTD/xhtml10strict.dtd\">\n"
        "<html lang=\"en\">\n"
        "<head>\n"
        "<title>%s</title>\n"
        "</head>\n"
        "<body>\n"
        "<h1>%d %s%s%s%s</h1>\n"
        "<hr />\n"
        "<a href=\"http://www.videolan.org\">VideoLAN</a>\n"
        "</body>\n"
        "</html>\n", errname, code, errname,
        (url ? " (" : ""), (url ?: ""), (url ? ")" : ""));

    if (res == -1)
    {
        *body = NULL;
        return 0;
    }

    return (size_t)res;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
376

377
/*****************************************************************************
378
 * High Level Functions: httpd_file_t
379
380
381
382
383
384
385
386
387
388
389
390
391
 *****************************************************************************/
struct httpd_file_t
{
    httpd_url_t *url;

    char *psz_url;
    char *psz_mime;

    httpd_file_callback_t pf_fill;
    httpd_file_sys_t      *p_sys;

};

392
393
394
static int
httpd_FileCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
                    httpd_message_t *answer, const httpd_message_t *query )
395
396
{
    httpd_file_t *file = (httpd_file_t*)p_sys;
397
398
    uint8_t *psz_args = query->psz_args;
    uint8_t **pp_body, *p_body;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
399
    const char *psz_connection;
400
    int *pi_body, i_body;
401
402
403
404
405
406

    if( answer == NULL || query == NULL )
    {
        return VLC_SUCCESS;
    }
    answer->i_proto  = HTTPD_PROTO_HTTP;
Rémi Denis-Courmont's avatar
Fixes    
Rémi Denis-Courmont committed
407
    answer->i_version= 1;
408
409
410
411
412
413
414
415
416
    answer->i_type   = HTTPD_MSG_ANSWER;

    answer->i_status = 200;

    httpd_MsgAdd( answer, "Content-type",  "%s", file->psz_mime );
    httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );

    if( query->i_type != HTTPD_MSG_HEAD )
    {
417
418
419
420
421
422
423
424
425
426
        pp_body = &answer->p_body;
        pi_body = &answer->i_body;
    }
    else
    {
        /* The file still needs to be executed. */
        p_body = NULL;
        i_body = 0;
        pp_body = &p_body;
        pi_body = &i_body;
427
    }
428
429
430
431
432
433
434
435
436
437
438
439
440

    if( query->i_type == HTTPD_MSG_POST )
    {
        /* msg_Warn not supported */
    }

    file->pf_fill( file->p_sys, file, psz_args, pp_body, pi_body );

    if( query->i_type == HTTPD_MSG_HEAD && p_body != NULL )
    {
        free( p_body );
    }

441
    /* We respect client request */
442
443
    psz_connection = httpd_MsgGet( &cl->query, "Connection" );
    if( psz_connection != NULL )
444
    {
445
        httpd_MsgAdd( answer, "Connection", "%s", psz_connection );
446
447
    }

448
449
450
451
452
453
    httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );

    return VLC_SUCCESS;
}

httpd_file_t *httpd_FileNew( httpd_host_t *host,
454
455
                             const char *psz_url, const char *psz_mime,
                             const char *psz_user, const char *psz_password,
456
                             const vlc_acl_t *p_acl, httpd_file_callback_t pf_fill,
457
458
459
460
                             httpd_file_sys_t *p_sys )
{
    httpd_file_t *file = malloc( sizeof( httpd_file_t ) );

461
    if( ( file->url = httpd_UrlNewUnique( host, psz_url, psz_user,
462
                                          psz_password, p_acl )
463
        ) == NULL )
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
    {
        free( file );
        return NULL;
    }

    file->psz_url  = strdup( psz_url );
    if( psz_mime && *psz_mime )
    {
        file->psz_mime = strdup( psz_mime );
    }
    else
    {
        file->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
    }

    file->pf_fill = pf_fill;
    file->p_sys   = p_sys;

482
483
484
485
486
487
    httpd_UrlCatch( file->url, HTTPD_MSG_HEAD, httpd_FileCallBack,
                    (httpd_callback_sys_t*)file );
    httpd_UrlCatch( file->url, HTTPD_MSG_GET,  httpd_FileCallBack,
                    (httpd_callback_sys_t*)file );
    httpd_UrlCatch( file->url, HTTPD_MSG_POST, httpd_FileCallBack,
                    (httpd_callback_sys_t*)file );
488
489
490
491

    return file;
}

492
httpd_file_sys_t *httpd_FileDelete( httpd_file_t *file )
493
{
494
495
    httpd_file_sys_t *p_sys = file->p_sys;

496
497
498
499
500
501
    httpd_UrlDelete( file->url );

    free( file->psz_url );
    free( file->psz_mime );

    free( file );
502
503

    return p_sys;
504
505
}

506
507
508
509
510
511
512
513
514
515
516
517
/*****************************************************************************
 * High Level Functions: httpd_handler_t (for CGIs)
 *****************************************************************************/
struct httpd_handler_t
{
    httpd_url_t *url;

    httpd_handler_callback_t pf_fill;
    httpd_handler_sys_t      *p_sys;

};

518
519
520
static int
httpd_HandlerCallBack( httpd_callback_sys_t *p_sys, httpd_client_t *cl,
                       httpd_message_t *answer, const httpd_message_t *query )
521
522
523
{
    httpd_handler_t *handler = (httpd_handler_t*)p_sys;
    uint8_t *psz_args = query->psz_args;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
524
    char psz_remote_addr[NI_MAXNUMERICHOST];
525
526
527
528
529
530
531
532
533
534
535

    if( answer == NULL || query == NULL )
    {
        return VLC_SUCCESS;
    }
    answer->i_proto  = HTTPD_PROTO_NONE;
    answer->i_type   = HTTPD_MSG_ANSWER;

    /* We do it ourselves, thanks */
    answer->i_status = 0;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
536
537
    if( httpd_ClientIP( cl, psz_remote_addr ) == NULL )
        *psz_remote_addr = '\0';
538
539
540
541
542
543
544
545

    handler->pf_fill( handler->p_sys, handler, query->psz_url, psz_args,
                      query->i_type, query->p_body, query->i_body,
                      psz_remote_addr, NULL,
                      &answer->p_body, &answer->i_body );

    if( query->i_type == HTTPD_MSG_HEAD )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
546
        char *p = (char *)answer->p_body;
547
548

        /* Looks for end of header (i.e. one empty line) */
549
550
551
552
553
554
555
556
        while ( (p = strchr( p, '\r' )) != NULL )
        {
            if( p[1] && p[1] == '\n' && p[2] && p[2] == '\r'
                 && p[3] && p[3] == '\n' )
            {
                break;
            }
        }
557

558
559
560
        if( p != NULL )
        {
            p[4] = '\0';
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
561
            answer->i_body = strlen((char*)answer->p_body) + 1;
562
563
564
565
            answer->p_body = realloc( answer->p_body, answer->i_body );
        }
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
566
    if( strncmp( (char *)answer->p_body, "HTTP/1.", 7 ) )
567
568
    {
        int i_status, i_headers;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
569
570
        char *psz_headers, *psz_new;
        const char *psz_status;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
571
572

        if( !strncmp( (char *)answer->p_body, "Status: ", 8 ) )
573
574
        {
            /* Apache-style */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
575
            i_status = strtol( (char *)&answer->p_body[8], &psz_headers, 0 );
576
577
578
579
580
581
582
            if( *psz_headers ) psz_headers++;
            if( *psz_headers ) psz_headers++;
            i_headers = answer->i_body - (psz_headers - (char *)answer->p_body);
        }
        else
        {
            i_status = 200;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
583
            psz_headers = (char *)answer->p_body;
584
585
            i_headers = answer->i_body;
        }
586
587

        psz_status = httpd_ReasonFromCode( i_status );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
588
589
590
591
        answer->i_body = sizeof("HTTP/1.0 xxx \r\n")
                        + strlen(psz_status) + i_headers - 1;
        psz_new = (char *)malloc( answer->i_body + 1);
        sprintf( psz_new, "HTTP/1.0 %03d %s\r\n", i_status, psz_status );
592
593
        memcpy( &psz_new[strlen(psz_new)], psz_headers, i_headers );
        free( answer->p_body );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
594
        answer->p_body = (uint8_t *)psz_new;
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
    }

    return VLC_SUCCESS;
}

httpd_handler_t *httpd_HandlerNew( httpd_host_t *host, const char *psz_url,
                                   const char *psz_user,
                                   const char *psz_password,
                                   const vlc_acl_t *p_acl,
                                   httpd_handler_callback_t pf_fill,
                                   httpd_handler_sys_t *p_sys )
{
    httpd_handler_t *handler = malloc( sizeof( httpd_handler_t ) );

    if( ( handler->url = httpd_UrlNewUnique( host, psz_url, psz_user,
                                             psz_password, p_acl )
        ) == NULL )
    {
        free( handler );
        return NULL;
    }

    handler->pf_fill = pf_fill;
    handler->p_sys   = p_sys;

    httpd_UrlCatch( handler->url, HTTPD_MSG_HEAD, httpd_HandlerCallBack,
                    (httpd_callback_sys_t*)handler );
    httpd_UrlCatch( handler->url, HTTPD_MSG_GET,  httpd_HandlerCallBack,
                    (httpd_callback_sys_t*)handler );
    httpd_UrlCatch( handler->url, HTTPD_MSG_POST, httpd_HandlerCallBack,
                    (httpd_callback_sys_t*)handler );

    return handler;
}

630
httpd_handler_sys_t *httpd_HandlerDelete( httpd_handler_t *handler )
631
{
632
    httpd_handler_sys_t *p_sys = handler->p_sys;
633
634
    httpd_UrlDelete( handler->url );
    free( handler );
635
    return p_sys;
636
637
}

638
/*****************************************************************************
639
 * High Level Functions: httpd_redirect_t
640
641
642
643
644
645
646
 *****************************************************************************/
struct httpd_redirect_t
{
    httpd_url_t *url;
    char        *psz_dst;
};

647
648
static int httpd_RedirectCallBack( httpd_callback_sys_t *p_sys,
                                   httpd_client_t *cl, httpd_message_t *answer,
649
                                   const httpd_message_t *query )
650
651
{
    httpd_redirect_t *rdir = (httpd_redirect_t*)p_sys;
652
    char *p_body;
653
    (void)cl;
654
655
656
657
658

    if( answer == NULL || query == NULL )
    {
        return VLC_SUCCESS;
    }
659
    answer->i_proto  = HTTPD_PROTO_HTTP;
Rémi Denis-Courmont's avatar
Fixes    
Rémi Denis-Courmont committed
660
    answer->i_version= 1;
661
662
663
    answer->i_type   = HTTPD_MSG_ANSWER;
    answer->i_status = 301;

664
    answer->i_body = httpd_HtmlError (&p_body, 301, rdir->psz_dst);
665
    answer->p_body = (unsigned char *)p_body;
666
667
668
669
670
671
672
673
674

    /* XXX check if it's ok or we need to set an absolute url */
    httpd_MsgAdd( answer, "Location",  "%s", rdir->psz_dst );

    httpd_MsgAdd( answer, "Content-Length", "%d", answer->i_body );

    return VLC_SUCCESS;
}

675
676
httpd_redirect_t *httpd_RedirectNew( httpd_host_t *host, const char *psz_url_dst,
                                     const char *psz_url_src )
677
678
679
{
    httpd_redirect_t *rdir = malloc( sizeof( httpd_redirect_t ) );

680
    if( !( rdir->url = httpd_UrlNewUnique( host, psz_url_src, NULL, NULL, NULL ) ) )
681
682
683
684
685
686
    {
        free( rdir );
        return NULL;
    }
    rdir->psz_dst = strdup( psz_url_dst );

687
688
689
690
691
692
693
694
695
    /* Redirect apply for all HTTP request and RTSP DESCRIBE resquest */
    httpd_UrlCatch( rdir->url, HTTPD_MSG_HEAD, httpd_RedirectCallBack,
                    (httpd_callback_sys_t*)rdir );
    httpd_UrlCatch( rdir->url, HTTPD_MSG_GET, httpd_RedirectCallBack,
                    (httpd_callback_sys_t*)rdir );
    httpd_UrlCatch( rdir->url, HTTPD_MSG_POST, httpd_RedirectCallBack,
                    (httpd_callback_sys_t*)rdir );
    httpd_UrlCatch( rdir->url, HTTPD_MSG_DESCRIBE, httpd_RedirectCallBack,
                    (httpd_callback_sys_t*)rdir );
696
697
698

    return rdir;
}
699
void httpd_RedirectDelete( httpd_redirect_t *rdir )
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
{
    httpd_UrlDelete( rdir->url );
    free( rdir->psz_dst );
    free( rdir );
}

/*****************************************************************************
 * High Level Funtions: httpd_stream_t
 *****************************************************************************/
struct httpd_stream_t
{
    vlc_mutex_t lock;
    httpd_url_t *url;

    char    *psz_mime;

    /* Header to send as first packet */
    uint8_t *p_header;
    int     i_header;

    /* circular buffer */
    int         i_buffer_size;      /* buffer size, can't be reallocated smaller */
    uint8_t     *p_buffer;          /* buffer */
    int64_t     i_buffer_pos;       /* absolute position from begining */
    int64_t     i_buffer_last_pos;  /* a new connection will start with that */
};

727
728
static int httpd_StreamCallBack( httpd_callback_sys_t *p_sys,
                                 httpd_client_t *cl, httpd_message_t *answer,
729
                                 const httpd_message_t *query )
730
731
732
733
734
735
736
{
    httpd_stream_t *stream = (httpd_stream_t*)p_sys;

    if( answer == NULL || query == NULL || cl == NULL )
    {
        return VLC_SUCCESS;
    }
Jean-Paul Saman's avatar
Jean-Paul Saman committed
737

738
739
740
741
742
    if( answer->i_body_offset > 0 )
    {
        int64_t i_write;
        int     i_pos;

743
744
745
746
#if 0
        fprintf( stderr, "httpd_StreamCallBack i_body_offset=%lld\n",
                 answer->i_body_offset );
#endif
747
748
749
750
751
752

        if( answer->i_body_offset >= stream->i_buffer_pos )
        {
            /* fprintf( stderr, "httpd_StreamCallBack: no data\n" ); */
            return VLC_EGENERIC;    /* wait, no data available */
        }
753
754
        if( answer->i_body_offset + stream->i_buffer_size <
            stream->i_buffer_pos )
755
756
        {
            /* this client isn't fast enough */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
757
#if 0
758
759
            fprintf( stderr, "fixing i_body_offset (old=%lld new=%lld)\n",
                     answer->i_body_offset, stream->i_buffer_last_pos );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
760
#endif
761
762
763
764
765
            answer->i_body_offset = stream->i_buffer_last_pos;
        }

        i_pos   = answer->i_body_offset % stream->i_buffer_size;
        i_write = stream->i_buffer_pos - answer->i_body_offset;
766
        if( i_write > HTTPD_CL_BUFSIZE )
767
        {
768
            i_write = HTTPD_CL_BUFSIZE;
769
770
771
772
773
774
        }
        else if( i_write <= 0 )
        {
            return VLC_EGENERIC;    /* wait, no data available */
        }

775
776
777
        /* Don't go past the end of the circular buffer */
        i_write = __MIN( i_write, stream->i_buffer_size - i_pos );

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
        /* using HTTPD_MSG_ANSWER -> data available */
        answer->i_proto  = HTTPD_PROTO_HTTP;
        answer->i_version= 0;
        answer->i_type   = HTTPD_MSG_ANSWER;

        answer->i_body = i_write;
        answer->p_body = malloc( i_write );
        memcpy( answer->p_body, &stream->p_buffer[i_pos], i_write );

        answer->i_body_offset += i_write;

        return VLC_SUCCESS;
    }
    else
    {
        answer->i_proto  = HTTPD_PROTO_HTTP;
        answer->i_version= 0;
        answer->i_type   = HTTPD_MSG_ANSWER;

        answer->i_status = 200;

        if( query->i_type != HTTPD_MSG_HEAD )
        {
            httpd_ClientModeStream( cl );
            vlc_mutex_lock( &stream->lock );
            /* Send the header */
            if( stream->i_header > 0 )
            {
                answer->i_body = stream->i_header;
                answer->p_body = malloc( stream->i_header );
                memcpy( answer->p_body, stream->p_header, stream->i_header );
            }
            answer->i_body_offset = stream->i_buffer_last_pos;
            vlc_mutex_unlock( &stream->lock );
        }
        else
        {
            httpd_MsgAdd( answer, "Content-Length", "%d", 0 );
816
            answer->i_body_offset = 0;
817
818
819
820
        }

        if( !strcmp( stream->psz_mime, "video/x-ms-asf-stream" ) )
        {
821
            bool b_xplaystream = false;
822
823
            int i;

824
825
            httpd_MsgAdd( answer, "Content-type", "%s",
                          "application/octet-stream" );
826
827
828
829
            httpd_MsgAdd( answer, "Server", "Cougar 4.1.0.3921" );
            httpd_MsgAdd( answer, "Pragma", "no-cache" );
            httpd_MsgAdd( answer, "Pragma", "client-id=%d", rand()&0x7fff );
            httpd_MsgAdd( answer, "Pragma", "features=\"broadcast\"" );
830

831
832
833
834
            /* Check if there is a xPlayStrm=1 */
            for( i = 0; i < query->i_name; i++ )
            {
                if( !strcasecmp( query->name[i],  "Pragma" ) &&
835
                    strstr( query->value[i], "xPlayStrm=1" ) )
836
                {
837
                    b_xplaystream = true;
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
                }
            }

            if( !b_xplaystream )
            {
                answer->i_body_offset = 0;
            }
        }
        else
        {
            httpd_MsgAdd( answer, "Content-type",  "%s", stream->psz_mime );
        }
        httpd_MsgAdd( answer, "Cache-Control", "%s", "no-cache" );
        return VLC_SUCCESS;
    }
}

httpd_stream_t *httpd_StreamNew( httpd_host_t *host,
856
857
                                 const char *psz_url, const char *psz_mime,
                                 const char *psz_user, const char *psz_password,
858
                                 const vlc_acl_t *p_acl )
859
860
861
{
    httpd_stream_t *stream = malloc( sizeof( httpd_stream_t ) );

862
    if( ( stream->url = httpd_UrlNewUnique( host, psz_url, psz_user,
863
                                            psz_password, p_acl )
864
        ) == NULL )
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
    {
        free( stream );
        return NULL;
    }
    vlc_mutex_init( host, &stream->lock );
    if( psz_mime && *psz_mime )
    {
        stream->psz_mime = strdup( psz_mime );
    }
    else
    {
        stream->psz_mime = strdup( httpd_MimeFromUrl( psz_url ) );
    }
    stream->i_header = 0;
    stream->p_header = NULL;
    stream->i_buffer_size = 5000000;    /* 5 Mo per stream */
    stream->p_buffer = malloc( stream->i_buffer_size );
882
883
    /* We set to 1 to make life simpler
     * (this way i_body_offset can never be 0) */
884
885
886
    stream->i_buffer_pos = 1;
    stream->i_buffer_last_pos = 1;

887
888
889
890
891
892
    httpd_UrlCatch( stream->url, HTTPD_MSG_HEAD, httpd_StreamCallBack,
                    (httpd_callback_sys_t*)stream );
    httpd_UrlCatch( stream->url, HTTPD_MSG_GET, httpd_StreamCallBack,
                    (httpd_callback_sys_t*)stream );
    httpd_UrlCatch( stream->url, HTTPD_MSG_POST, httpd_StreamCallBack,
                    (httpd_callback_sys_t*)stream );
893
894
895
896

    return stream;
}

897
int httpd_StreamHeader( httpd_stream_t *stream, uint8_t *p_data, int i_data )
898
899
{
    vlc_mutex_lock( &stream->lock );
900
901
902
    free( stream->p_header );
    stream->p_header = NULL;

903
904
905
906
907
908
909
910
911
912
913
    stream->i_header = i_data;
    if( i_data > 0 )
    {
        stream->p_header = malloc( i_data );
        memcpy( stream->p_header, p_data, i_data );
    }
    vlc_mutex_unlock( &stream->lock );

    return VLC_SUCCESS;
}

914
int httpd_StreamSend( httpd_stream_t *stream, uint8_t *p_data, int i_data )
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
{
    int i_count;
    int i_pos;

    if( i_data < 0 || p_data == NULL )
    {
        return VLC_SUCCESS;
    }
    vlc_mutex_lock( &stream->lock );

    /* save this pointer (to be used by new connection) */
    stream->i_buffer_last_pos = stream->i_buffer_pos;

    i_pos = stream->i_buffer_pos % stream->i_buffer_size;
    i_count = i_data;
    while( i_count > 0)
    {
        int i_copy;

        i_copy = __MIN( i_count, stream->i_buffer_size - i_pos );

        /* Ok, we can't go past the end of our buffer */
        memcpy( &stream->p_buffer[i_pos], p_data, i_copy );

        i_pos = ( i_pos + i_copy ) % stream->i_buffer_size;
        i_count -= i_copy;
        p_data  += i_copy;
    }

    stream->i_buffer_pos += i_data;

    vlc_mutex_unlock( &stream->lock );
    return VLC_SUCCESS;
}

void httpd_StreamDelete( httpd_stream_t *stream )
{
    httpd_UrlDelete( stream->url );
    vlc_mutex_destroy( &stream->lock );
954
955
956
    free( stream->psz_mime );
    free( stream->p_header );
    free( stream->p_buffer );
957
958
959
960
961
962
963
964
965
    free( stream );
}

/*****************************************************************************
 * Low level
 *****************************************************************************/
static void httpd_HostThread( httpd_host_t * );

/* create a new host */
966
httpd_host_t *httpd_HostNew( vlc_object_t *p_this, const char *psz_host,
967
968
                             int i_port )
{
969
970
    return httpd_TLSHostNew( p_this, psz_host, i_port, NULL, NULL, NULL, NULL
                           );
971
972
}

973
974
static const char psz_object_type[] = "http server";

975
976
977
978
httpd_host_t *httpd_TLSHostNew( vlc_object_t *p_this, const char *psz_hostname,
                                int i_port,
                                const char *psz_cert, const char *psz_key,
                                const char *psz_ca, const char *psz_crl )
979
980
{
    httpd_t      *httpd;
981
    httpd_host_t *host;
982
983
    tls_server_t *p_tls;
    char *psz_host;
984
    vlc_value_t  lockval, ptrval;
985
    int i;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
986

987
988
989
    if( psz_hostname == NULL )
        psz_hostname = "";

990
    psz_host = strdup( psz_hostname );
991
    if( psz_host == NULL )
992
    {
993
        msg_Err( p_this, "memory error" );
994
995
996
997
        return NULL;
    }

    /* to be sure to avoid multiple creation */
998
    var_Create( p_this->p_libvlc, "httpd_mutex", VLC_VAR_MUTEX );
999
    var_Create( p_this->p_libvlc, "httpd_object", VLC_VAR_ADDRESS );
1000
    var_Get( p_this->p_libvlc, "httpd_mutex", &lockval );
1001
    vlc_mutex_lock( lockval.p_address );
1002
    var_Get( p_this->p_libvlc, "httpd_object", &ptrval );
1003

1004
1005
1006
    if( ptrval.p_address != NULL )
        httpd = ptrval.p_address;
    else
1007
1008
    {
        msg_Info( p_this, "creating httpd" );
1009
        httpd = (httpd_t *)vlc_custom_create( p_this, sizeof (*httpd),
1010
                                              VLC_OBJECT_GENERIC,
1011
                                              psz_object_type );
1012
        if( httpd == NULL )
1013
1014
        {
            vlc_mutex_unlock( lockval.p_address );
1015
            free( psz_host );
1016
1017
1018
1019
1020
1021
            return NULL;
        }

        httpd->i_host = 0;
        httpd->host   = NULL;

1022
1023
        ptrval.p_address = httpd;
        var_Set( p_this->p_libvlc, "httpd_object", ptrval );
1024
        vlc_object_yield( httpd );
1025
        vlc_object_attach( httpd, p_this->p_libvlc );
1026
1027
    }

1028
1029
    /* verify if it already exist */
    for( i = httpd->i_host - 1; i >= 0; i-- )
1030
    {
1031
        host = httpd->host[i];
1032

1033
1034
1035
1036
        /* cannot mix TLS and non-TLS hosts */
        if( ( ( httpd->host[i]->p_tls != NULL ) != ( psz_cert != NULL ) )
         || ( host->i_port != i_port )
         || strcmp( host->psz_hostname, psz_hostname ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1037
1038
            continue;

1039
1040
        /* yep found */
        host->i_ref++;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1041

1042
1043
        vlc_mutex_unlock( lockval.p_address );
        return host;
1044
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1045

1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
    host = NULL;

    /* determine TLS configuration */
    if ( psz_cert != NULL )
    {
        p_tls = tls_ServerCreate( p_this, psz_cert, psz_key );
        if ( p_tls == NULL )
        {
            msg_Err( p_this, "TLS initialization error" );
            goto error;
        }

        if ( ( psz_ca != NULL) && tls_ServerAddCA( p_tls, psz_ca ) )
        {
            msg_Err( p_this, "TLS CA error" );
            goto error;
        }

        if ( ( psz_crl != NULL) && tls_ServerAddCRL( p_tls, psz_crl ) )
        {
            msg_Err( p_this, "TLS CRL error" );
            goto error;
        }
    }
    else
        p_tls = NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1073
    /* create the new host */
1074
1075
1076
1077
1078
1079
    host = (httpd_host_t *)vlc_custom_create( p_this, sizeof (*host),
                                              VLC_OBJECT_HTTPD_HOST,
                                              psz_object_type );
    if (host == NULL)
        goto error;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1080
    vlc_object_lock( host );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1081
    if( vlc_object_waitpipe( VLC_OBJECT( host ) ) == -1 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1082
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1083
        msg_Err( host, "signaling pipe error: %m" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1084
1085
1086
1087
1088
        vlc_object_unlock( host );
        goto error;
    }
    vlc_object_unlock( host );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1089
1090
1091
1092
    host->httpd = httpd;
    vlc_mutex_init( httpd, &host->lock );
    host->i_ref = 1;

1093
1094
    host->fds = net_ListenTCP( p_this, psz_host, i_port );
    if( host->fds == NULL )
1095
1096
1097
1098
    {
        msg_Err( p_this, "cannot create socket(s) for HTTP host" );
        goto error;
    }
1099
    for (host->nfd = 0; host->fds[host->nfd] != -1; host->nfd++);
1100

1101
1102
1103
    host->i_port = i_port;
    host->psz_hostname = psz_host;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1104
1105
1106
1107
    host->i_url     = 0;
    host->url       = NULL;
    host->i_client  = 0;
    host->client    = NULL;
1108

1109
    host->p_tls = p_tls;
1110

1111
    /* create the thread */
1112
    if( vlc_thread_create( host, "httpd host thread", httpd_HostThread,
1113
                           VLC_THREAD_PRIORITY_LOW, false ) )
1114
1115
    {
        msg_Err( p_this, "cannot spawn http host thread" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1116
        goto error;
1117
1118
1119
1120
1121
1122
1123
1124
    }

    /* now add it to httpd */
    TAB_APPEND( httpd->i_host, httpd->host, host );
    vlc_mutex_unlock( lockval.p_address );

    return host;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1125
error:
1126
    free( psz_host );
1127
1128
1129
1130
    if( httpd->i_host <= 0 )
    {
        vlc_object_release( httpd );
        vlc_object_detach( httpd );
1131
        vlc_object_release( httpd );
1132
    }
1133
1134
    vlc_mutex_unlock( lockval.p_address );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1135
    if( host != NULL )
1136
    {
1137
        net_ListenClose( host->fds );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1138
        vlc_mutex_destroy( &host->lock );
1139
        vlc_object_release( host );
1140
1141
    }

1142
1143
1144
    if( p_tls != NULL )
        tls_ServerDelete( p_tls );

1145
1146
1147
1148
    return NULL;
}

/* delete a host */
1149
void httpd_HostDelete( httpd_host_t *host )
1150
1151
1152
1153
1154
{
    httpd_t *httpd = host->httpd;
    vlc_value_t lockval;
    int i;

1155
    var_Get( httpd->p_libvlc, "httpd_mutex", &lockval );
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
    vlc_mutex_lock( lockval.p_address );

    host->i_ref--;
    if( host->i_ref > 0 )
    {
        /* still used */
        vlc_mutex_unlock( lockval.p_address );
        msg_Dbg( host, "httpd_HostDelete: host still used" );
        return;
    }
    TAB_REMOVE( httpd->i_host, httpd->host, host );

1168
    vlc_object_kill( host );
1169
1170
    vlc_thread_join( host );

zorglub's avatar
zorglub committed
1171
    msg_Dbg( host, "HTTP host removed" );
1172
1173
1174

    for( i = 0; i < host->i_url; i++ )
    {
1175
        msg_Err( host, "url still registered: %s", host->url[i]->psz_url );
1176
1177
1178
1179
1180
    }
    for( i = 0; i < host->i_client; i++ )
    {
        httpd_client_t *cl = host->client[i];
        msg_Warn( host, "client still connected" );
1181
1182
1183
1184
        httpd_ClientClean( cl );
        TAB_REMOVE( host->i_client, host->client, cl );
        free( cl );
        i--;
1185
1186
1187
        /* TODO */
    }

1188
1189
    if( host->p_tls != NULL)
        tls_ServerDelete( host->p_tls );
1190

1191
    net_ListenClose( host->fds );
1192
1193
    free( host->psz_hostname );

1194
    vlc_mutex_destroy( &host->lock );
1195
    vlc_object_release( host );
1196

1197
    vlc_object_release( httpd );
1198
1199
    if( httpd->i_host <= 0 )
    {
1200
1201
        vlc_value_t ptrval;

zorglub's avatar
zorglub committed
1202
        msg_Dbg( httpd, "no host left, stopping httpd" );
1203
1204
1205

        ptrval.p_address = NULL;
        var_Set( httpd->p_libvlc, "httpd_object", ptrval );
1206
        vlc_object_detach( httpd );
1207
        vlc_object_release( httpd );
1208

1209
1210
1211
1212
1213
    }
    vlc_mutex_unlock( lockval.p_address );
}

/* register a new url */
1214
1215
static httpd_url_t *httpd_UrlNewPrivate( httpd_host_t *host, const char *psz_url,
                                         const char *psz_user, const char *psz_password,
1216
                                         const vlc_acl_t *p_acl, bool b_check )
1217
1218
1219
1220
{
    httpd_url_t *url;
    int         i;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1221
1222
    assert( psz_url != NULL );

1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
    vlc_mutex_lock( &host->lock );
    if( b_check )
    {
        for( i = 0; i < host->i_url; i++ )
        {
            if( !strcmp( psz_url, host->url[i]->psz_url ) )
            {
                msg_Warn( host->httpd,
                          "cannot add '%s' (url already defined)", psz_url );
                vlc_mutex_unlock( &host->lock );
                return NULL;
            }
        }
    }

    url = malloc( sizeof( httpd_url_t ) );
    url->host = host;

    vlc_mutex_init( host->httpd, &url->lock );
    url->psz_url = strdup( psz_url );
    url->psz_user = strdup( psz_user ? psz_user : "" );
    url->psz_password = strdup( psz_password ? psz_password : "" );
1245
    url->p_acl = ACL_Duplicate( host, p_acl );
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
    for( i = 0; i < HTTPD_MSG_MAX; i++ )
    {
        url->catch[i].cb = NULL;
        url->catch[i].p_sys = NULL;
    }

    TAB_APPEND( host->i_url, host->url, url );
    vlc_mutex_unlock( &host->lock );

    return url;
}

1258
1259
httpd_url_t *httpd_UrlNew( httpd_host_t *host, const char *psz_url,
                           const char *psz_user, const char *psz_password,
1260
                           const vlc_acl_t *p_acl )
1261
{
1262
    return httpd_UrlNewPrivate( host, psz_url, psz_user,
1263
                                psz_password, p_acl, false );
1264
1265
}

1266
1267
httpd_url_t *httpd_UrlNewUnique( httpd_host_t *host, const char *psz_url,
                                 const char *psz_user, const char *psz_password,
1268
                                 const vlc_acl_t *p_acl )
1269
{
1270
    return httpd_UrlNewPrivate( host, psz_url, psz_user,
1271
                                psz_password, p_acl, true );
1272
1273
1274
}

/* register callback on a url */
1275
1276
int httpd_UrlCatch( httpd_url_t *url, int i_msg, httpd_callback_t cb,
                    httpd_callback_sys_t *p_sys )
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
{
    vlc_mutex_lock( &url->lock );
    url->catch[i_msg].cb   = cb;
    url->catch[i_msg].p_sys= p_sys;
    vlc_mutex_unlock( &url->lock );

    return VLC_SUCCESS;
}

/* delete an url */
1287
void httpd_UrlDelete( httpd_url_t *url )
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
{
    httpd_host_t *host = url->host;
    int          i;

    vlc_mutex_lock( &host->lock );
    TAB_REMOVE( host->i_url, host->url, url );

    vlc_mutex_destroy( &url->lock );
    free( url->psz_url );
    free( url->psz_user );
    free( url->psz_password );
1299
    ACL_Destroy( url->p_acl );
1300
1301
1302
1303
1304
1305
1306
1307
1308

    for( i = 0; i < host->i_client; i++ )
    {
        httpd_client_t *client = host->client[i];

        if( client->url == url )
        {
            /* TODO complete it */
            msg_Warn( host, "force closing connections" );
1309
            httpd_ClientClean( client );
1310
            TAB_REMOVE( host->i_client, host->client, client );
1311
            free( client );
1312
1313
1314
            i--;
        }
    }
1315
    free( url );
1316
1317
1318
1319
1320
1321
1322
1323
    vlc_mutex_unlock( &host->lock );
}

void httpd_MsgInit( httpd_message_t *msg )
{
    msg->cl         = NULL;
    msg->i_type     = HTTPD_MSG_NONE;
    msg->i_proto    = HTTPD_PROTO_NONE;
1324
    msg->i_version  = -1; /* FIXME */
1325
1326
1327

    msg->i_status   = 0;

1328
1329
    msg->psz_url    = NULL;
    msg->psz_args   = NULL;
1330

1331
    msg->i_channel  = -1;
1332