1 /* Icecast
2  *
3  * This program is distributed under the GNU General Public License, version 2.
4  * A copy of this license is included with this source.
5  *
6  * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7  *                      Michael Smith <msmith@xiph.org>,
8  *                      oddsock <oddsock@xiph.org>,
9  *                      Karl Heyes <karl@xiph.org>
10  *                      and others (see AUTHORS for details).
11  * Copyright 2011,      Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>.
12  */
13 
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #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 #ifndef _WIN32
30 #include <unistd.h>
31 #include <sys/time.h>
32 #include <sys/socket.h>
33 #define SCN_OFF_T SCNdMAX
34 #define PRI_OFF_T PRIdMAX
35 #else
36 #include <winsock2.h>
37 #include <windows.h>
38 #define fseeko fseek
39 #define SCN_OFF_T "ld"
40 #define PRI_OFF_T "ld"
41 #define snprintf _snprintf
42 #define strncasecmp _strnicmp
43 #ifndef S_ISREG
44 #define S_ISREG(mode)  ((mode) & _S_IFREG)
45 #endif
46 #endif
47 
48 #include "thread/thread.h"
49 #include "avl/avl.h"
50 #include "httpp/httpp.h"
51 #include "net/sock.h"
52 
53 #include "connection.h"
54 #include "global.h"
55 #include "refbuf.h"
56 #include "client.h"
57 #include "stats.h"
58 #include "format.h"
59 #include "logging.h"
60 #include "cfgfile.h"
61 #include "util.h"
62 #include "admin.h"
63 #include "compat.h"
64 
65 #include "fserve.h"
66 
67 #undef CATMODULE
68 #define CATMODULE "fserve"
69 
70 #define BUFSIZE 4096
71 
72 static volatile int __inited = 0;
73 
74 static fserve_t *active_list = NULL;
75 static fserve_t *pending_list = NULL;
76 
77 static spin_t pending_lock;
78 static avl_tree *mimetypes = NULL;
79 
80 static volatile int run_fserv = 0;
81 static unsigned int fserve_clients;
82 static int client_tree_changed=0;
83 
84 #ifdef HAVE_POLL
85 static struct pollfd *ufds = NULL;
86 #else
87 static fd_set fds;
88 static sock_t fd_max = SOCK_ERROR;
89 #endif
90 
91 typedef struct {
92     char *ext;
93     char *type;
94 } mime_type;
95 
96 static void fserve_client_destroy(fserve_t *fclient);
97 static int _delete_mapping(void *mapping);
98 static void *fserv_thread_function(void *arg);
99 
fserve_initialize(void)100 void fserve_initialize(void)
101 {
102     ice_config_t *config = config_get_config();
103 
104     mimetypes = NULL;
105     active_list = NULL;
106     pending_list = NULL;
107     thread_spin_create (&pending_lock);
108 
109     fserve_recheck_mime_types (config);
110     config_release_config();
111 
112     __inited = 1;
113 
114     stats_event (NULL, "file_connections", "0");
115     ICECAST_LOG_INFO("file serving started");
116 }
117 
fserve_shutdown(void)118 void fserve_shutdown(void)
119 {
120     if (!__inited)
121         return;
122 
123     thread_spin_lock (&pending_lock);
124     run_fserv = 0;
125     while (pending_list)
126     {
127         fserve_t *to_go = (fserve_t *)pending_list;
128         pending_list = to_go->next;
129 
130         fserve_client_destroy (to_go);
131     }
132     while (active_list)
133     {
134         fserve_t *to_go = active_list;
135         active_list = to_go->next;
136         fserve_client_destroy (to_go);
137     }
138 
139     if (mimetypes)
140         avl_tree_free (mimetypes, _delete_mapping);
141 
142     thread_spin_unlock (&pending_lock);
143     thread_spin_destroy (&pending_lock);
144     ICECAST_LOG_INFO("file serving stopped");
145 }
146 
147 #ifdef HAVE_POLL
fserve_client_waiting(void)148 int fserve_client_waiting (void)
149 {
150     fserve_t *fclient;
151     unsigned int i = 0;
152 
153     /* only rebuild ufds if there are clients added/removed */
154     if (client_tree_changed)
155     {
156         client_tree_changed = 0;
157         ufds = realloc(ufds, fserve_clients * sizeof(struct pollfd));
158         fclient = active_list;
159         while (fclient)
160         {
161             ufds[i].fd = fclient->client->con->sock;
162             ufds[i].events = POLLOUT;
163             ufds[i].revents = 0;
164             fclient = fclient->next;
165             i++;
166         }
167     }
168     if (!ufds)
169     {
170         thread_spin_lock (&pending_lock);
171         run_fserv = 0;
172         thread_spin_unlock (&pending_lock);
173         return -1;
174     }
175     else if (poll(ufds, fserve_clients, 200) > 0)
176     {
177         /* mark any clients that are ready */
178         fclient = active_list;
179         for (i=0; i<fserve_clients; i++)
180         {
181             if (ufds[i].revents & (POLLOUT|POLLHUP|POLLERR))
182                 fclient->ready = 1;
183             fclient = fclient->next;
184         }
185         return 1;
186     }
187     return 0;
188 }
189 #else
fserve_client_waiting(void)190 int fserve_client_waiting (void)
191 {
192     fserve_t *fclient;
193     fd_set realfds;
194 
195     /* only rebuild fds if there are clients added/removed */
196     if(client_tree_changed) {
197         client_tree_changed = 0;
198         FD_ZERO(&fds);
199         fd_max = SOCK_ERROR;
200         fclient = active_list;
201         while (fclient) {
202             FD_SET (fclient->client->con->sock, &fds);
203             if (fclient->client->con->sock > fd_max || fd_max == SOCK_ERROR)
204                 fd_max = fclient->client->con->sock;
205             fclient = fclient->next;
206         }
207     }
208     /* hack for windows, select needs at least 1 descriptor */
209     if (fd_max == SOCK_ERROR)
210     {
211         thread_spin_lock (&pending_lock);
212         run_fserv = 0;
213         thread_spin_unlock (&pending_lock);
214         return -1;
215     }
216     else
217     {
218         struct timeval tv;
219         tv.tv_sec = 0;
220         tv.tv_usec = 200000;
221         /* make a duplicate of the set so we do not have to rebuild it
222          * each time around */
223         memcpy(&realfds, &fds, sizeof(fd_set));
224         if(select(fd_max+1, NULL, &realfds, NULL, &tv) > 0)
225         {
226             /* mark any clients that are ready */
227             fclient = active_list;
228             while (fclient)
229             {
230                 if (FD_ISSET (fclient->client->con->sock, &realfds))
231                     fclient->ready = 1;
232                 fclient = fclient->next;
233             }
234             return 1;
235         }
236     }
237     return 0;
238 }
239 #endif
240 
wait_for_fds(void)241 static int wait_for_fds(void)
242 {
243     fserve_t *fclient;
244     int ret;
245 
246     while (run_fserv)
247     {
248         /* add any new clients here */
249         if (pending_list)
250         {
251             thread_spin_lock (&pending_lock);
252 
253             fclient = (fserve_t*)pending_list;
254             while (fclient)
255             {
256                 fserve_t *to_move = fclient;
257                 fclient = fclient->next;
258                 to_move->next = active_list;
259                 active_list = to_move;
260                 client_tree_changed = 1;
261                 fserve_clients++;
262             }
263             pending_list = NULL;
264             thread_spin_unlock (&pending_lock);
265         }
266         /* drop out of here if someone is ready */
267         ret = fserve_client_waiting();
268         if (ret)
269             return ret;
270     }
271     return -1;
272 }
273 
fserv_thread_function(void * arg)274 static void *fserv_thread_function(void *arg)
275 {
276     fserve_t *fclient, **trail;
277     size_t bytes;
278 
279     while (1)
280     {
281         if (wait_for_fds() < 0)
282             break;
283 
284         fclient = active_list;
285         trail = &active_list;
286 
287         while (fclient)
288         {
289             /* process this client, if it is ready */
290             if (fclient->ready)
291             {
292                 client_t *client = fclient->client;
293                 refbuf_t *refbuf = client->refbuf;
294                 fclient->ready = 0;
295                 if (client->pos == refbuf->len)
296                 {
297                     /* Grab a new chunk */
298                     if (fclient->file)
299                         bytes = fread (refbuf->data, 1, BUFSIZE, fclient->file);
300                     else
301                         bytes = 0;
302                     if (bytes == 0)
303                     {
304                         if (refbuf->next == NULL)
305                         {
306                             fserve_t *to_go = fclient;
307                             fclient = fclient->next;
308                             *trail = fclient;
309                             fserve_client_destroy (to_go);
310                             fserve_clients--;
311                             client_tree_changed = 1;
312                             continue;
313                         }
314                         refbuf = refbuf->next;
315                         client->refbuf->next = NULL;
316                         refbuf_release (client->refbuf);
317                         client->refbuf = refbuf;
318                         bytes = refbuf->len;
319                     }
320                     refbuf->len = (unsigned int)bytes;
321                     client->pos = 0;
322                 }
323 
324                 /* Now try and send current chunk. */
325                 format_generic_write_to_client (client);
326 
327                 if (client->con->error)
328                 {
329                     fserve_t *to_go = fclient;
330                     fclient = fclient->next;
331                     *trail = fclient;
332                     fserve_clients--;
333                     fserve_client_destroy (to_go);
334                     client_tree_changed = 1;
335                     continue;
336                 }
337             }
338             trail = &fclient->next;
339             fclient = fclient->next;
340         }
341     }
342     ICECAST_LOG_DEBUG("fserve handler exit");
343     return NULL;
344 }
345 
346 /* string returned needs to be free'd */
fserve_content_type(const char * path)347 char *fserve_content_type (const char *path)
348 {
349     char *ext = util_get_extension(path);
350     mime_type exttype = {ext, NULL};
351     void *result;
352     char *type;
353 
354     thread_spin_lock (&pending_lock);
355     if (mimetypes && !avl_get_by_key (mimetypes, &exttype, &result))
356     {
357         mime_type *mime = result;
358         type = strdup (mime->type);
359     }
360     else {
361         /* Fallbacks for a few basic ones */
362         if(!strcmp(ext, "ogg"))
363             type = strdup ("application/ogg");
364         else if(!strcmp(ext, "mp3"))
365             type = strdup ("audio/mpeg");
366         else if(!strcmp(ext, "html"))
367             type = strdup ("text/html");
368         else if(!strcmp(ext, "css"))
369             type = strdup ("text/css");
370         else if(!strcmp(ext, "txt"))
371             type = strdup ("text/plain");
372         else if(!strcmp(ext, "jpg"))
373             type = strdup ("image/jpeg");
374         else if(!strcmp(ext, "png"))
375             type = strdup ("image/png");
376         else if(!strcmp(ext, "m3u"))
377             type = strdup ("audio/x-mpegurl");
378         else if(!strcmp(ext, "aac"))
379             type = strdup ("audio/aac");
380         else
381             type = strdup ("application/octet-stream");
382     }
383     thread_spin_unlock (&pending_lock);
384     return type;
385 }
386 
fserve_client_destroy(fserve_t * fclient)387 static void fserve_client_destroy(fserve_t *fclient)
388 {
389     if (fclient)
390     {
391         if (fclient->file)
392             fclose (fclient->file);
393 
394         if (fclient->callback)
395             fclient->callback (fclient->client, fclient->arg);
396         else
397             if (fclient->client)
398                 client_destroy (fclient->client);
399         free (fclient);
400     }
401 }
402 
403 
404 /* client has requested a file, so check for it and send the file.  Do not
405  * refer to the client_t afterwards.  return 0 for success, -1 on error.
406  */
fserve_client_create(client_t * httpclient,const char * path)407 int fserve_client_create (client_t *httpclient, const char *path)
408 {
409     int bytes;
410     struct stat file_buf;
411     const char *range = NULL;
412     off_t new_content_len = 0;
413     off_t rangenumber = 0, content_length;
414     int rangeproblem = 0;
415     int ret = 0;
416     char *fullpath;
417     int m3u_requested = 0, m3u_file_available = 1;
418     const char * xslt_playlist_requested = NULL;
419     int xslt_playlist_file_available = 1;
420     ice_config_t *config;
421     FILE *file;
422 
423     fullpath = util_get_path_from_normalised_uri (path);
424     ICECAST_LOG_INFO("checking for file %H (%H)", path, fullpath);
425 
426     if (strcmp (util_get_extension (fullpath), "m3u") == 0)
427         m3u_requested = 1;
428 
429     if (strcmp (util_get_extension (fullpath), "xspf") == 0)
430         xslt_playlist_requested = "xspf.xsl";
431 
432     if (strcmp (util_get_extension (fullpath), "vclt") == 0)
433         xslt_playlist_requested = "vclt.xsl";
434 
435     /* check for the actual file */
436     if (stat (fullpath, &file_buf) != 0)
437     {
438         /* the m3u can be generated, but send an m3u file if available */
439         if (m3u_requested == 0 && xslt_playlist_requested == NULL)
440         {
441             ICECAST_LOG_WARN("req for file \"%H\" %s", fullpath, strerror (errno));
442             client_send_404 (httpclient, "The file you requested could not be found");
443             free (fullpath);
444             return -1;
445         }
446         m3u_file_available = 0;
447         xslt_playlist_file_available = 0;
448     }
449 
450     httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE;
451 
452     if (m3u_requested && m3u_file_available == 0)
453     {
454         const char *host = httpp_getvar (httpclient->parser, "host");
455         char *sourceuri = strdup (path);
456         char *dot = strrchr(sourceuri, '.');
457 
458         /* at least a couple of players (fb2k/winamp) are reported to send a
459          * host header but without the port number. So if we are missing the
460          * port then lets treat it as if no host line was sent */
461         if (host && strchr (host, ':') == NULL)
462             host = NULL;
463 
464         *dot = 0;
465         httpclient->respcode = 200;
466         ret = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
467 	                              0, 200, NULL,
468 				      "audio/x-mpegurl", NULL, "", NULL);
469         if (ret == -1 || ret >= (BUFSIZE - 512)) { /* we want at least 512 bytes left for the content of the playlist */
470             ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
471             client_send_500(httpclient, "Header generation failed.");
472             return -1;
473         }
474         if (host == NULL)
475         {
476 	    config = config_get_config();
477             snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
478                     "http://%s:%d%s\r\n",
479                     config->hostname, config->port,
480                     sourceuri
481                     );
482             config_release_config();
483         }
484         else
485         {
486 	    snprintf (httpclient->refbuf->data + ret, BUFSIZE - ret,
487                     "http://%s%s\r\n",
488                     host,
489                     sourceuri
490                     );
491         }
492         httpclient->refbuf->len = strlen (httpclient->refbuf->data);
493         fserve_add_client (httpclient, NULL);
494         free (sourceuri);
495         free (fullpath);
496         return 0;
497     }
498     if (xslt_playlist_requested && xslt_playlist_file_available == 0)
499     {
500         xmlDocPtr doc;
501         char *reference = strdup (path);
502         char *eol = strrchr (reference, '.');
503         if (eol)
504             *eol = '\0';
505         doc = stats_get_xml (0, reference);
506         free (reference);
507         admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested);
508         xmlFreeDoc(doc);
509         return 0;
510     }
511 
512     /* on demand file serving check */
513     config = config_get_config();
514     if (config->fileserve == 0)
515     {
516         ICECAST_LOG_DEBUG("on demand file \"%H\" refused. Serving static files has been disabled in the config", fullpath);
517         client_send_404 (httpclient, "The file you requested could not be found");
518         config_release_config();
519         free (fullpath);
520         return -1;
521     }
522     config_release_config();
523 
524     if (S_ISREG (file_buf.st_mode) == 0)
525     {
526         client_send_404 (httpclient, "The file you requested could not be found");
527         ICECAST_LOG_WARN("found requested file but there is no handler for it: %H", fullpath);
528         free (fullpath);
529         return -1;
530     }
531 
532     file = fopen (fullpath, "rb");
533     if (file == NULL)
534     {
535         ICECAST_LOG_WARN("Problem accessing file \"%H\"", fullpath);
536         client_send_404 (httpclient, "File not readable");
537         free (fullpath);
538         return -1;
539     }
540     free (fullpath);
541 
542     content_length = file_buf.st_size;
543     range = httpp_getvar (httpclient->parser, "range");
544 
545     /* full http range handling is currently not done but we deal with the common case */
546     if (range != NULL) {
547         ret = 0;
548         if (strncasecmp (range, "bytes=", 6) == 0)
549             ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber);
550 
551         if (ret != 1) {
552             /* format not correct, so lets just assume
553                we start from the beginning */
554             rangeproblem = 1;
555         }
556         if (rangenumber < 0) {
557             rangeproblem = 1;
558         }
559         if (!rangeproblem) {
560             ret = fseeko (file, rangenumber, SEEK_SET);
561             if (ret != -1) {
562                 new_content_len = content_length - rangenumber;
563                 if (new_content_len < 0) {
564                     rangeproblem = 1;
565                 }
566             }
567             else {
568                 rangeproblem = 1;
569             }
570             if (!rangeproblem) {
571                 off_t endpos = rangenumber+new_content_len-1;
572                 char *type;
573 
574                 if (endpos < 0) {
575                     endpos = 0;
576                 }
577                 httpclient->respcode = 206;
578                 type = fserve_content_type (path);
579 		bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
580 		                                0, 206, NULL,
581 						type, NULL,
582 						NULL, NULL);
583                 if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
584                     ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
585                     client_send_500(httpclient, "Header generation failed.");
586                     return -1;
587                 }
588                 bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
589                     "Accept-Ranges: bytes\r\n"
590                     "Content-Length: %" PRI_OFF_T "\r\n"
591                     "Content-Range: bytes %" PRI_OFF_T \
592                     "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n\r\n",
593                     new_content_len,
594                     rangenumber,
595                     endpos,
596                     content_length);
597                 free (type);
598             }
599             else {
600                 goto fail;
601             }
602         }
603         else {
604             goto fail;
605         }
606     }
607     else {
608         char *type = fserve_content_type(path);
609         httpclient->respcode = 200;
610 	bytes = util_http_build_header (httpclient->refbuf->data, BUFSIZE, 0,
611 	                                0, 200, NULL,
612 					type, NULL,
613 					NULL, NULL);
614         if (bytes == -1 || bytes >= (BUFSIZE - 512)) { /* we want at least 512 bytes left */
615             ICECAST_LOG_ERROR("Dropping client as we can not build response headers.");
616             client_send_500(httpclient, "Header generation failed.");
617             return -1;
618         }
619         bytes += snprintf (httpclient->refbuf->data + bytes, BUFSIZE - bytes,
620             "Accept-Ranges: bytes\r\n"
621             "Content-Length: %" PRI_OFF_T "\r\n\r\n",
622             content_length);
623         free (type);
624     }
625     httpclient->refbuf->len = bytes;
626     httpclient->pos = 0;
627 
628     stats_event_inc (NULL, "file_connections");
629     fserve_add_client (httpclient, file);
630 
631     return 0;
632 
633 fail:
634     fclose (file);
635     client_send_error(httpclient, 416, 1, "Request Range Not Satisfiable\r\n");
636     return -1;
637 }
638 
639 
640 /* Routine to actually add pre-configured client structure to pending list and
641  * then to start off the file serving thread if it is not already running
642  */
fserve_add_pending(fserve_t * fclient)643 static void fserve_add_pending (fserve_t *fclient)
644 {
645     thread_spin_lock (&pending_lock);
646     fclient->next = (fserve_t *)pending_list;
647     pending_list = fclient;
648     if (run_fserv == 0)
649     {
650         run_fserv = 1;
651         ICECAST_LOG_DEBUG("fserve handler waking up");
652         thread_create("File Serving Thread", fserv_thread_function, NULL, THREAD_DETACHED);
653     }
654     thread_spin_unlock (&pending_lock);
655 }
656 
657 
658 /* Add client to fserve thread, client needs to have refbuf set and filled
659  * but may provide a NULL file if no data needs to be read
660  */
fserve_add_client(client_t * client,FILE * file)661 int fserve_add_client (client_t *client, FILE *file)
662 {
663     fserve_t *fclient = calloc (1, sizeof(fserve_t));
664 
665     ICECAST_LOG_DEBUG("Adding client to file serving engine");
666     if (fclient == NULL)
667     {
668         client_send_404 (client, "memory exhausted");
669         return -1;
670     }
671     fclient->file = file;
672     fclient->client = client;
673     fclient->ready = 0;
674     fserve_add_pending (fclient);
675 
676     return 0;
677 }
678 
679 
680 /* add client to file serving engine, but just write out the buffer contents,
681  * then pass the client to the callback with the provided arg
682  */
fserve_add_client_callback(client_t * client,fserve_callback_t callback,void * arg)683 void fserve_add_client_callback (client_t *client, fserve_callback_t callback, void *arg)
684 {
685     fserve_t *fclient = calloc (1, sizeof(fserve_t));
686 
687     ICECAST_LOG_DEBUG("Adding client to file serving engine");
688     if (fclient == NULL)
689     {
690         client_send_404 (client, "memory exhausted");
691         return;
692     }
693     fclient->file = NULL;
694     fclient->client = client;
695     fclient->ready = 0;
696     fclient->callback = callback;
697     fclient->arg = arg;
698 
699     fserve_add_pending (fclient);
700 }
701 
702 
_delete_mapping(void * mapping)703 static int _delete_mapping(void *mapping) {
704     mime_type *map = mapping;
705     free(map->ext);
706     free(map->type);
707     free(map);
708 
709     return 1;
710 }
711 
_compare_mappings(void * arg,void * a,void * b)712 static int _compare_mappings(void *arg, void *a, void *b)
713 {
714     return strcmp(
715             ((mime_type *)a)->ext,
716             ((mime_type *)b)->ext);
717 }
718 
fserve_recheck_mime_types(ice_config_t * config)719 void fserve_recheck_mime_types (ice_config_t *config)
720 {
721     FILE *mimefile;
722     char line[4096];
723     char *type, *ext, *cur;
724     mime_type *mapping;
725     avl_tree *new_mimetypes;
726 
727     if (config->mimetypes_fn == NULL)
728         return;
729     mimefile = fopen (config->mimetypes_fn, "r");
730     if (mimefile == NULL)
731     {
732         ICECAST_LOG_WARN("Cannot open mime types file %s", config->mimetypes_fn);
733         return;
734     }
735 
736     new_mimetypes = avl_tree_new(_compare_mappings, NULL);
737 
738     while(fgets(line, 4096, mimefile))
739     {
740         line[4095] = 0;
741 
742         if(*line == 0 || *line == '#')
743             continue;
744 
745         type = line;
746 
747         cur = line;
748 
749         while(*cur != ' ' && *cur != '\t' && *cur)
750             cur++;
751         if(*cur == 0)
752             continue;
753 
754         *cur++ = 0;
755 
756         while(1) {
757             while(*cur == ' ' || *cur == '\t')
758                 cur++;
759             if(*cur == 0)
760                 break;
761 
762             ext = cur;
763             while(*cur != ' ' && *cur != '\t' && *cur != '\n' && *cur)
764                 cur++;
765             *cur++ = 0;
766             if(*ext)
767             {
768                 void *tmp;
769                 /* Add a new extension->type mapping */
770                 mapping = malloc(sizeof(mime_type));
771                 mapping->ext = strdup(ext);
772                 mapping->type = strdup(type);
773                 if (!avl_get_by_key (new_mimetypes, mapping, &tmp))
774                     avl_delete (new_mimetypes, mapping, _delete_mapping);
775                 avl_insert (new_mimetypes, mapping);
776             }
777         }
778     }
779     fclose(mimefile);
780 
781     thread_spin_lock (&pending_lock);
782     if (mimetypes)
783         avl_tree_free (mimetypes, _delete_mapping);
784     mimetypes = new_mimetypes;
785     thread_spin_unlock (&pending_lock);
786 }
787 
788