magic.c revision 251881
1/*
2 * magic.c:  wrappers around libmagic
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24/* ==================================================================== */
25
26
27/*** Includes. ***/
28
29#include <apr_lib.h>
30#include <apr_file_info.h>
31
32#include "svn_io.h"
33#include "svn_types.h"
34#include "svn_pools.h"
35#include "svn_error.h"
36
37#include "svn_private_config.h"
38
39#include "private/svn_magic.h"
40
41#ifdef SVN_HAVE_LIBMAGIC
42#include <magic.h>
43#endif
44
45struct svn_magic__cookie_t {
46#ifdef SVN_HAVE_LIBMAGIC
47  magic_t magic;
48#else
49  char dummy;
50#endif
51};
52
53#ifdef SVN_HAVE_LIBMAGIC
54/* Close the magic database. */
55static apr_status_t
56close_magic_cookie(void *baton)
57{
58  svn_magic__cookie_t *mc = (svn_magic__cookie_t*)baton;
59  magic_close(mc->magic);
60  return APR_SUCCESS;
61}
62#endif
63
64void
65svn_magic__init(svn_magic__cookie_t **magic_cookie,
66                apr_pool_t *result_pool)
67{
68
69  svn_magic__cookie_t *mc = NULL;
70
71#ifdef SVN_HAVE_LIBMAGIC
72  mc = apr_palloc(result_pool, sizeof(*mc));
73
74  /* Initialise libmagic. */
75#ifndef MAGIC_MIME_TYPE
76  /* Some old versions of libmagic don't support MAGIC_MIME_TYPE.
77   * We can use MAGIC_MIME instead. It returns more than we need
78   * but we can work around that (see below). */
79  mc->magic = magic_open(MAGIC_MIME | MAGIC_ERROR);
80#else
81  mc->magic = magic_open(MAGIC_MIME_TYPE | MAGIC_ERROR);
82#endif
83  if (mc->magic)
84    {
85      /* This loads the default magic database.
86       * Point the MAGIC environment variable at your favourite .mgc
87       * file to load a non-default database. */
88      if (magic_load(mc->magic, NULL) == -1)
89        {
90          magic_close(mc->magic);
91          mc = NULL;
92        }
93      else
94        apr_pool_cleanup_register(result_pool, mc, close_magic_cookie,
95                                  apr_pool_cleanup_null);
96    }
97#endif
98
99  *magic_cookie = mc;
100}
101
102svn_error_t *
103svn_magic__detect_binary_mimetype(const char **mimetype,
104                                  const char *local_abspath,
105                                  svn_magic__cookie_t *magic_cookie,
106                                  apr_pool_t *result_pool,
107                                  apr_pool_t *scratch_pool)
108{
109  const char *magic_mimetype = NULL;
110#ifdef SVN_HAVE_LIBMAGIC
111  apr_finfo_t finfo;
112
113  /* Do not ask libmagic for the mime-types of empty files.
114   * This prevents mime-types like "application/x-empty" from making
115   * Subversion treat empty files as binary. */
116  SVN_ERR(svn_io_stat(&finfo, local_abspath, APR_FINFO_SIZE, scratch_pool));
117  if (finfo.size > 0)
118    {
119      magic_mimetype = magic_file(magic_cookie->magic, local_abspath);
120      if (magic_mimetype)
121        {
122          /* Only return binary mime-types. */
123          if (strncmp(magic_mimetype, "text/", 5) == 0)
124            magic_mimetype = NULL;
125          else
126            {
127              svn_error_t *err;
128#ifndef MAGIC_MIME_TYPE
129              char *p;
130
131              /* Strip off trailing stuff like " charset=ascii". */
132              p = strchr(magic_mimetype, ' ');
133              if (p)
134                *p = '\0';
135#endif
136              /* Make sure we got a valid mime type. */
137              err = svn_mime_type_validate(magic_mimetype, scratch_pool);
138              if (err)
139                {
140                  if (err->apr_err == SVN_ERR_BAD_MIME_TYPE)
141                    {
142                      svn_error_clear(err);
143                      magic_mimetype = NULL;
144                    }
145                  else
146                    return svn_error_trace(err);
147                }
148              else
149                {
150                  /* The string is allocated from memory managed by libmagic
151                   * so we must copy it to the result pool. */
152                  magic_mimetype = apr_pstrdup(result_pool, magic_mimetype);
153                }
154            }
155        }
156    }
157#endif
158
159  *mimetype = magic_mimetype;
160  return SVN_NO_ERROR;
161}
162