fserve.c 22.4 KB
Newer Older
1 2 3 4 5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 8 9 10
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
Philipp Schafft's avatar
Philipp Schafft committed
11
 * Copyright 2011,      Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>.
12 13
 */

14 15 16 17
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

18 19 20 21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
22
#include <sys/stat.h>
23
#include <errno.h>
24 25

#ifdef HAVE_POLL
26
#include <sys/poll.h>
27
#endif
28 29 30 31 32

#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
33 34
#define SCN_OFF_T SCNdMAX
#define PRI_OFF_T PRIdMAX
35 36 37
#else
#include <winsock2.h>
#include <windows.h>
38 39
#define SCN_OFF_T "ld"
#define PRI_OFF_T "ld"
40
#ifndef S_ISREG
41
#define S_ISREG(mode)  ((mode) & _S_IFREG)
42
#endif
43
#endif
44

Marvin Scholz's avatar
Marvin Scholz committed
45 46 47 48
#include "common/thread/thread.h"
#include "common/avl/avl.h"
#include "common/httpp/httpp.h"
#include "common/net/sock.h"
49 50 51 52 53 54 55 56

#include "connection.h"
#include "global.h"
#include "refbuf.h"
#include "client.h"
#include "stats.h"
#include "format.h"
#include "logging.h"
57
#include "cfgfile.h"
58
#include "util.h"
59
#include "admin.h"
60
#include "compat.h"
61 62 63 64 65 66 67 68

#include "fserve.h"

#undef CATMODULE
#define CATMODULE "fserve"

#define BUFSIZE 4096

69 70
static volatile int __inited = 0;

71 72
static fserve_t *active_list = NULL;
static fserve_t *pending_list = NULL;
73

74
static spin_t pending_lock;
75
static avl_tree *mimetypes = NULL;
76

77
static volatile int run_fserv = 0;
78
static unsigned int fserve_clients;
79
static int client_tree_changed = 0;
80

81
#ifdef HAVE_POLL
82
static struct pollfd *ufds = NULL;
83 84
#else
static fd_set fds;
85
static sock_t fd_max = SOCK_ERROR;
86
#endif
87

88 89 90 91 92
typedef struct {
    char *ext;
    char *type;
} mime_type;

93
static void fserve_client_destroy(fserve_t *fclient);
94 95
static int _delete_mapping(void *mapping);
static void *fserv_thread_function(void *arg);
96 97 98

void fserve_initialize(void)
{
99
    ice_config_t *config = config_get_config();
100

101
    mimetypes = NULL;
102 103 104
    active_list = NULL;
    pending_list = NULL;
    thread_spin_create (&pending_lock);
105

106 107 108
    fserve_recheck_mime_types (config);
    config_release_config();

109 110
    __inited = 1;

111
    stats_event (NULL, "file_connections", "0");
112
    ICECAST_LOG_INFO("file serving started");
113 114 115 116
}

void fserve_shutdown(void)
{
117 118 119
    if (!__inited)
        return;

120
    thread_spin_lock (&pending_lock);
121
    run_fserv = 0;
122 123 124 125 126 127 128 129 130 131 132 133 134 135
    while (pending_list)
    {
        fserve_t *to_go = (fserve_t *)pending_list;
        pending_list = to_go->next;

        fserve_client_destroy (to_go);
    }
    while (active_list)
    {
        fserve_t *to_go = active_list;
        active_list = to_go->next;
        fserve_client_destroy (to_go);
    }

136 137
    if (mimetypes)
        avl_tree_free (mimetypes, _delete_mapping);
138

139 140
    thread_spin_unlock (&pending_lock);
    thread_spin_destroy (&pending_lock);
141
    ICECAST_LOG_INFO("file serving stopped");
142 143
}

144
#ifdef HAVE_POLL
145 146 147 148
int fserve_client_waiting (void)
{
    fserve_t *fclient;
    unsigned int i = 0;
149

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
    /* only rebuild ufds if there are clients added/removed */
    if (client_tree_changed)
    {
        client_tree_changed = 0;
        ufds = realloc(ufds, fserve_clients * sizeof(struct pollfd));
        fclient = active_list;
        while (fclient)
        {
            ufds[i].fd = fclient->client->con->sock;
            ufds[i].events = POLLOUT;
            ufds[i].revents = 0;
            fclient = fclient->next;
            i++;
        }
    }
brendan's avatar
brendan committed
165
    if (!ufds)
166
    {
167
        thread_spin_lock (&pending_lock);
168
        run_fserv = 0;
169
        thread_spin_unlock (&pending_lock);
170 171
        return -1;
    }
brendan's avatar
brendan committed
172
    else if (poll(ufds, fserve_clients, 200) > 0)
173 174 175 176 177 178
    {
        /* mark any clients that are ready */
        fclient = active_list;
        for (i=0; i<fserve_clients; i++)
        {
            if (ufds[i].revents & (POLLOUT|POLLHUP|POLLERR))
179
            fclient->ready = 1;
180 181 182 183 184 185
            fclient = fclient->next;
        }
        return 1;
    }
    return 0;
}
186
#else
187
int fserve_client_waiting(void)
188 189 190 191 192
{
    fserve_t *fclient;
    fd_set realfds;

    /* only rebuild fds if there are clients added/removed */
193
    if (client_tree_changed) {
194 195
        client_tree_changed = 0;
        FD_ZERO(&fds);
196
        fd_max = SOCK_ERROR;
197 198
        fclient = active_list;
        while (fclient) {
199
            FD_SET(fclient->client->con->sock, &fds);
200
            if (fclient->client->con->sock > fd_max || fd_max == SOCK_ERROR)
201 202 203 204 205
                fd_max = fclient->client->con->sock;
            fclient = fclient->next;
        }
    }
    /* hack for windows, select needs at least 1 descriptor */
206
    if (fd_max == SOCK_ERROR)
207
    {
208
        thread_spin_lock (&pending_lock);
209
        run_fserv = 0;
210
        thread_spin_unlock (&pending_lock);
211 212
        return -1;
    }
213 214
    else
    {
215 216 217
        struct timeval tv;
        tv.tv_sec = 0;
        tv.tv_usec = 200000;
218 219
        /* make a duplicate of the set so we do not have to rebuild it
         * each time around */
220 221
        memcpy(&realfds, &fds, sizeof(fd_set));
        if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0)
222 223 224 225 226 227 228 229 230 231 232 233 234 235
        {
            /* mark any clients that are ready */
            fclient = active_list;
            while (fclient)
            {
                if (FD_ISSET (fclient->client->con->sock, &realfds))
                    fclient->ready = 1;
                fclient = fclient->next;
            }
            return 1;
        }
    }
    return 0;
}
236
#endif
237

238 239
static int wait_for_fds(void)
{
240
    fserve_t *fclient;
241
    int ret;
242 243 244 245 246 247

    while (run_fserv)
    {
        /* add any new clients here */
        if (pending_list)
        {
248
            thread_spin_lock (&pending_lock);
249 250 251 252 253 254 255 256 257 258 259 260

            fclient = (fserve_t*)pending_list;
            while (fclient)
            {
                fserve_t *to_move = fclient;
                fclient = fclient->next;
                to_move->next = active_list;
                active_list = to_move;
                client_tree_changed = 1;
                fserve_clients++;
            }
            pending_list = NULL;
261
            thread_spin_unlock(&pending_lock);
262
        }
263
        /* drop out of here if someone is ready */
264 265 266
        ret = fserve_client_waiting();
        if (ret)
            return ret;
267
    }
268
    return -1;
269 270
}

271
static void *fserv_thread_function(void *arg)
272
{
273
    fserve_t *fclient, **trail;
274
    size_t bytes;
275

276 277 278 279
    while (1)
    {
        if (wait_for_fds() < 0)
            break;
280

281 282 283 284 285 286 287 288
        fclient = active_list;
        trail = &active_list;

        while (fclient)
        {
            /* process this client, if it is ready */
            if (fclient->ready)
            {
289 290
                client_t *client = fclient->client;
                refbuf_t *refbuf = client->refbuf;
291
                fclient->ready = 0;
292 293
                if (client->pos == refbuf->len)
                {
294
                    /* Grab a new chunk */
295 296 297 298
                    if (fclient->file)
                        bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
                    else
                        bytes = 0;
299 300
                    if (bytes == 0)
                    {
301 302 303 304 305 306 307 308 309 310
                        if (refbuf->next == NULL)
                        {
                            fserve_t *to_go = fclient;
                            fclient = fclient->next;
                            *trail = fclient;
                            fserve_client_destroy (to_go);
                            fserve_clients--;
                            client_tree_changed = 1;
                            continue;
                        }
311
                        refbuf = refbuf->next;
312
                        client->refbuf->next = NULL;
313 314
                        refbuf_release (client->refbuf);
                        client->refbuf = refbuf;
315
                        bytes = refbuf->len;
316
                    }
317
                    refbuf->len = (unsigned int)bytes;
318
                    client->pos = 0;
319
                }
320

321
                /* Now try and send current chunk. */
322
                format_generic_write_to_client (client);
323

324
                if (client->con->error)
325 326 327 328 329
                {
                    fserve_t *to_go = fclient;
                    fclient = fclient->next;
                    *trail = fclient;
                    fserve_clients--;
330
                    fserve_client_destroy (to_go);
331
                    client_tree_changed = 1;
332 333 334
                    continue;
                }
            }
335 336
            trail = &fclient->next;
            fclient = fclient->next;
337 338
        }
    }
339
    ICECAST_LOG_DEBUG("fserve handler exit");
340 341 342
    return NULL;
}

343
/* string returned needs to be free'd */
344
char *fserve_content_type(const char *path)
345 346
{
    char *ext = util_get_extension(path);
347
    mime_type exttype = {ext, NULL};
348
    void *result;
349
    char *type;
350

351
    thread_spin_lock (&pending_lock);
352
    if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result))
353 354
    {
        mime_type *mime = result;
355
        type = strdup (mime->type);
356
    }
357 358 359
    else {
        /* Fallbacks for a few basic ones */
        if(!strcmp(ext, "ogg"))
360
            type = strdup ("application/ogg");
361
        else if(!strcmp(ext, "mp3"))
362
            type = strdup ("audio/mpeg");
363
        else if(!strcmp(ext, "html"))
364
            type = strdup ("text/html");
Karl Heyes's avatar
Karl Heyes committed
365
        else if(!strcmp(ext, "css"))
366
            type = strdup ("text/css");
367
        else if(!strcmp(ext, "txt"))
368
            type = strdup ("text/plain");
369
        else if(!strcmp(ext, "jpg"))
370
            type = strdup ("image/jpeg");
371
        else if(!strcmp(ext, "png"))
372
            type = strdup ("image/png");
373
        else if(!strcmp(ext, "m3u"))
374 375 376
            type = strdup ("audio/x-mpegurl");
        else if(!strcmp(ext, "aac"))
            type = strdup ("audio/aac");
377
        else
378
            type = strdup ("application/octet-stream");
379
    }
380
    thread_spin_unlock (&pending_lock);
381
    return type;
382 383
}

384
static void fserve_client_destroy(fserve_t *fclient)
385
{
386 387 388 389 390
    if (fclient)
    {
        if (fclient->file)
            fclose (fclient->file);

391 392 393 394 395
        if (fclient->callback)
            fclient->callback (fclient->client, fclient->arg);
        else
            if (fclient->client)
                client_destroy (fclient->client);
396
        free (fclient);
397 398 399
    }
}

400

401 402 403 404
/* client has requested a file, so check for it and send the file.  Do not
 * refer to the client_t afterwards.  return 0 for success, -1 on error.
 */
int fserve_client_create (client_t *httpclient, const char *path)
405
{
406
    int bytes;
407
    struct stat file_buf;
408
    const char *range = NULL;
409 410
    off_t new_content_len = 0;
    off_t rangenumber = 0, content_length;
411 412
    int rangeproblem = 0;
    int ret = 0;
413 414
    char *fullpath;
    int m3u_requested = 0, m3u_file_available = 1;
Philipp Schafft's avatar
Philipp Schafft committed
415 416
    const char * xslt_playlist_requested = NULL;
    int xslt_playlist_file_available = 1;
417 418 419 420
    ice_config_t *config;
    FILE *file;

    fullpath = util_get_path_from_normalised_uri (path);
421
    ICECAST_LOG_INFO("checking for file %H (%H)", path, fullpath);
422 423 424 425

    if (strcmp (util_get_extension (fullpath), "m3u") == 0)
        m3u_requested = 1;

426
    if (strcmp (util_get_extension (fullpath), "xspf") == 0)
Philipp Schafft's avatar
Philipp Schafft committed
427 428 429 430
        xslt_playlist_requested = "xspf.xsl";

    if (strcmp (util_get_extension (fullpath), "vclt") == 0)
        xslt_playlist_requested = "vclt.xsl";
431

432 433 434 435
    /* check for the actual file */
    if (stat (fullpath, &file_buf) != 0)
    {
        /* the m3u can be generated, but send an m3u file if available */
Philipp Schafft's avatar
Philipp Schafft committed
436
        if (m3u_requested == 0 && xslt_playlist_requested == NULL)
437
        {
438
            ICECAST_LOG_WARN("req for file \"%H\" %s", fullpath, strerror (errno));
439
            client_send_error(httpclient, 404, 0, "The file you requested could not be found");
440
            free (fullpath);
441
            return -1;
442 443
        }
        m3u_file_available = 0;
Philipp Schafft's avatar
Philipp Schafft committed
444
        xslt_playlist_file_available = 0;
445 446
    }

447
    httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;
448 449 450

    if (m3u_requested && m3u_file_available == 0)
    {
451
        const char *host = httpp_getvar (httpclient->parser, "host");
452 453
        char *sourceuri = strdup (path);
        char *dot = strrchr(sourceuri, '.');
454

455
        /* at least a couple of players (fb2k/winamp) are reported to send a
456 457 458 459 460
         * host header but without the port number. So if we are missing the
         * port then lets treat it as if no host line was sent */
        if (host && strchr (host, ':') == NULL)
            host = NULL;

461 462
        *dot = 0;
        httpclient->respcode = 200;
463
        ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
464
                                      0, 200, NULL,
465
                                      "audio/x-mpegurl", NULL, "", NULL, httpclient);
466 467
        if (ret == -1 || ret >= (BUFSIZE - 512)) { /* we want at least 512 bytes left for the content of the playlist */
            ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
468
            client_send_error(httpclient, 500, 0, "Header generation failed.");
469 470
            return -1;
        }
471 472
        if (host == NULL)
        {
473
            config = config_get_config();
474
            snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
475
                    "http://%s:%d%s\r\n",
476 477 478 479 480 481 482
                    config->hostname, config->port,
                    sourceuri
                    );
            config_release_config();
        }
        else
        {
483
            snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
484 485
                    "http://%s%s\r\n",
                    host,
486 487 488 489 490 491 492
                    sourceuri
                    );
        }
        httpclient->refbuf->len = strlen (httpclient->refbuf->data);
        fserve_add_client (httpclient, NULL);
        free (sourceuri);
        free (fullpath);
493
        return 0;
494
    }
Philipp Schafft's avatar
Philipp Schafft committed
495
    if (xslt_playlist_requested && xslt_playlist_file_available == 0)
496 497 498 499 500 501
    {
        xmlDocPtr doc;
        char *reference = strdup (path);
        char *eol = strrchr (reference, '.');
        if (eol)
            *eol = '\0';
502
        doc = stats_get_xml (0, reference, httpclient->mode);
503
        free (reference);
Philipp Schafft's avatar
Philipp Schafft committed
504
        admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested);
505
        xmlFreeDoc(doc);
506
        free (fullpath);
507 508
        return 0;
    }
Michael Smith's avatar
Michael Smith committed
509

510 511 512
    /* on demand file serving check */
    config = config_get_config();
    if (config->fileserve == 0)
513
    {
514
        ICECAST_LOG_DEBUG("on demand file \"%H\" refused. Serving static files has been disabled in the config", fullpath);
515
        client_send_error(httpclient, 404, 0, "The file you requested could not be found");
516
        config_release_config();
517
        free(fullpath);
518
        return -1;
519
    }
520
    config_release_config();
521

522
    if (S_ISREG (file_buf.st_mode) == 0)
523
    {
524
        client_send_error(httpclient, 404, 0, "The file you requested could not be found");
525
        ICECAST_LOG_WARN("found requested file but there is no handler for it: %H", fullpath);
526
        free (fullpath);
527
        return -1;
528
    }
529 530 531

    file = fopen (fullpath, "rb");
    if (file == NULL)
532
    {
533
        ICECAST_LOG_WARN("Problem accessing file \"%H\"", fullpath);
534
        client_send_error(httpclient, 404, 0, "File not readable");
535
        free (fullpath);
536
        return -1;
537
    }
538
    free (fullpath);
539

540
    content_length = file_buf.st_size;
541
    range = httpp_getvar (httpclient->parser, "range");
542

543
    /* full http range handling is currently not done but we deal with the common case */
544
    if (range != NULL) {
545 546
        ret = 0;
        if (strncasecmp (range, "bytes=", 6) == 0)
547
            ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber);
548

549 550 551 552 553 554 555 556 557
        if (ret != 1) {
            /* format not correct, so lets just assume
               we start from the beginning */
            rangeproblem = 1;
        }
        if (rangenumber < 0) {
            rangeproblem = 1;
        }
        if (!rangeproblem) {
558
            ret = fseeko (file, rangenumber, SEEK_SET);
559
            if (ret != -1) {
560
                new_content_len = content_length - rangenumber;
561 562 563 564 565 566 567 568
                if (new_content_len < 0) {
                    rangeproblem = 1;
                }
            }
            else {
                rangeproblem = 1;
            }
            if (!rangeproblem) {
569
                off_t endpos = rangenumber+new_content_len-1;
570 571
                char *type;

572 573 574
                if (endpos < 0) {
                    endpos = 0;
                }
575
                httpclient->respcode = 206;
576
                type = fserve_content_type (path);
577 578 579
                bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
                                                0, 206, NULL,
                                                type, NULL,
580
                                                NULL, NULL, httpclient);
581 582
                if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
                    ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
583
                    client_send_error(httpclient, 500, 0, "Header generation failed.");
584 585
                    return -1;
                }
586
                bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
587
                    "Accept-Ranges: bytes\r\n"
588 589
                    "Content-Length: %" PRI_OFF_T "\r\n"
                    "Content-Range: bytes %" PRI_OFF_T \
590
                    "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n",
591 592
                    new_content_len,
                    rangenumber,
593
                    endpos,
594
                    content_length);
595
                free (type);
596 597
            }
            else {
598
                goto fail;
599 600 601
            }
        }
        else {
602
            goto fail;
603 604 605
        }
    }
    else {
606
        char *type = fserve_content_type(path);
607
        httpclient->respcode = 200;
608 609 610
        bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
                                        0, 200, NULL,
                                        type, NULL,
611
                                        NULL, NULL, httpclient);
612 613
        if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
            ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
614
            client_send_error(httpclient, 500, 0, "Header generation failed.");
615 616
            return -1;
        }
617
        bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
618
            "Accept-Ranges: bytes\r\n"
619 620
            "Content-Length: %" PRI_OFF_T "\r\n\r\n",
            content_length);
621
        free (type);
622
    }
623
    httpclient->refbuf->len = bytes;
624 625
    httpclient->pos = 0;

626
    stats_event_inc (NULL, "file_connections");
627 628
    fserve_add_client (httpclient, file);

629
    return 0;
630 631 632 633

fail:
    fclose (file);
    httpclient->respcode = 416;
634
    sock_write (httpclient->con->sock,
635 636 637
            "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n");
    client_destroy (httpclient);
    return -1;
638 639 640
}


641 642 643 644 645
/* Routine to actually add pre-configured client structure to pending list and
 * then to start off the file serving thread if it is not already running
 */
static void fserve_add_pending (fserve_t *fclient)
{
646
    thread_spin_lock (&pending_lock);
647 648 649 650 651
    fclient->next = (fserve_t *)pending_list;
    pending_list = fclient;
    if (run_fserv == 0)
    {
        run_fserv = 1;
652
        ICECAST_LOG_DEBUG("fserve handler waking up");
653 654
        thread_create("File Serving Thread", fserv_thread_function, NULL, THREAD_DETACHED);
    }
655
    thread_spin_unlock (&pending_lock);
656 657 658
}


659 660 661 662 663 664 665
/* Add client to fserve thread, client needs to have refbuf set and filled
 * but may provide a NULL file if no data needs to be read
 */
int fserve_add_client (client_t *client, FILE *file)
{
    fserve_t *fclient = calloc (1, sizeof(fserve_t));

666
    ICECAST_LOG_DEBUG("Adding client to file serving engine");
667 668
    if (fclient == NULL)
    {
669
        client_send_error(client, 404, 0, "memory exhausted");
670 671 672 673 674
        return -1;
    }
    fclient->file = file;
    fclient->client = client;
    fclient->ready = 0;
675
    fserve_add_pending (fclient);
676

677
    return 0;
678 679 680
}


681 682 683 684 685 686 687
/* add client to file serving engine, but just write out the buffer contents,
 * then pass the client to the callback with the provided arg
 */
void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg)
{
    fserve_t *fclient = calloc (1, sizeof(fserve_t));

688
    ICECAST_LOG_DEBUG("Adding client to file serving engine");
689 690
    if (fclient == NULL)
    {
691
        client_send_error(client, 404, 0, "memory exhausted");
692 693 694 695 696 697 698 699
        return;
    }
    fclient->file = NULL;
    fclient->client = client;
    fclient->ready = 0;
    fclient->callback = callback;
    fclient->arg = arg;

700
    fserve_add_pending(fclient);
701 702 703
}


704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
static int _delete_mapping(void *mapping) {
    mime_type *map = mapping;
    free(map->ext);
    free(map->type);
    free(map);

    return 1;
}

static int _compare_mappings(void *arg, void *a, void *b)
{
    return strcmp(
            ((mime_type *)a)->ext,
            ((mime_type *)b)->ext);
}

720
void fserve_recheck_mime_types(ice_config_t *config)
721 722
{
    FILE *mimefile;
723 724
    char line[4096];
    char *type, *ext, *cur;
725
    mime_type *mapping;
726
    avl_tree *new_mimetypes;
727

728 729 730
    if (config->mimetypes_fn == NULL)
        return;
    mimefile = fopen (config->mimetypes_fn, "r");
731 732
    if (mimefile == NULL)
    {
733
        ICECAST_LOG_WARN("Cannot open mime types file %s", config->mimetypes_fn);
734
        return;
735
    }
736

737 738
    new_mimetypes = avl_tree_new(_compare_mappings, NULL);

739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
    while(fgets(line, 4096, mimefile))
    {
        line[4095] = 0;

        if(*line == 0 || *line == '#')
            continue;

        type = line;

        cur = line;

        while(*cur != ' ' && *cur != '\t' && *cur)
            cur++;
        if(*cur == 0)
            continue;

        *cur++ = 0;

        while(1) {
            while(*cur == ' ' || *cur == '\t')
                cur++;
            if(*cur == 0)
                break;

            ext = cur;
            while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
                cur++;
            *cur++ = 0;
767 768 769
            if(*ext)
            {
                void *tmp;
770 771 772 773
                /* Add a new extension->type mapping */
                mapping = malloc(sizeof(mime_type));
                mapping->ext = strdup(ext);
                mapping->type = strdup(type);
774 775 776
                if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
                    avl_delete (new_mimetypes, mapping, _delete_mapping);
                avl_insert (new_mimetypes, mapping);
777 778 779 780
            }
        }
    }
    fclose(mimefile);
781

782
    thread_spin_lock (&pending_lock);
783 784 785
    if (mimetypes)
        avl_tree_free (mimetypes, _delete_mapping);
    mimetypes = new_mimetypes;
786
    thread_spin_unlock (&pending_lock);
787 788
}