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 #include "svn_config.h"
37 #include "svn_hash.h"
38
39 #include "svn_private_config.h"
40
41 #include "private/svn_magic.h"
42
43 #ifdef SVN_HAVE_LIBMAGIC
44 #include <magic.h>
45 #endif
46
47 struct svn_magic__cookie_t {
48 #ifdef SVN_HAVE_LIBMAGIC
49 magic_t magic;
50 #else
51 char dummy;
52 #endif
53 };
54
55 #ifdef SVN_HAVE_LIBMAGIC
56 /* Close the magic database. */
57 static apr_status_t
close_magic_cookie(void * baton)58 close_magic_cookie(void *baton)
59 {
60 svn_magic__cookie_t *mc = (svn_magic__cookie_t*)baton;
61 magic_close(mc->magic);
62 return APR_SUCCESS;
63 }
64 #endif
65
66 svn_error_t *
svn_magic__init(svn_magic__cookie_t ** magic_cookie,apr_hash_t * config,apr_pool_t * result_pool)67 svn_magic__init(svn_magic__cookie_t **magic_cookie,
68 apr_hash_t *config,
69 apr_pool_t *result_pool)
70 {
71 svn_magic__cookie_t *mc = NULL;
72
73 #ifdef SVN_HAVE_LIBMAGIC
74 if (config)
75 {
76 svn_boolean_t enable;
77 svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
78
79 SVN_ERR(svn_config_get_bool(cfg, &enable,
80 SVN_CONFIG_SECTION_MISCELLANY,
81 SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE,
82 TRUE));
83 if (!enable)
84 {
85 *magic_cookie = NULL;
86 return SVN_NO_ERROR;
87 }
88 }
89
90 mc = apr_palloc(result_pool, sizeof(*mc));
91
92 /* Initialise libmagic. */
93 #ifndef MAGIC_MIME_TYPE
94 /* Some old versions of libmagic don't support MAGIC_MIME_TYPE.
95 * We can use MAGIC_MIME instead. It returns more than we need
96 * but we can work around that (see below). */
97 mc->magic = magic_open(MAGIC_MIME | MAGIC_ERROR);
98 #else
99 mc->magic = magic_open(MAGIC_MIME_TYPE | MAGIC_ERROR);
100 #endif
101 if (mc->magic)
102 {
103 /* This loads the default magic database.
104 * Point the MAGIC environment variable at your favourite .mgc
105 * file to load a non-default database. */
106 if (magic_load(mc->magic, NULL) == -1)
107 {
108 magic_close(mc->magic);
109 mc = NULL;
110 }
111 else
112 apr_pool_cleanup_register(result_pool, mc, close_magic_cookie,
113 apr_pool_cleanup_null);
114 }
115 #endif
116
117 *magic_cookie = mc;
118
119 return SVN_NO_ERROR;
120 }
121
122 svn_error_t *
svn_magic__detect_binary_mimetype(const char ** mimetype,const char * local_abspath,svn_magic__cookie_t * magic_cookie,apr_pool_t * result_pool,apr_pool_t * scratch_pool)123 svn_magic__detect_binary_mimetype(const char **mimetype,
124 const char *local_abspath,
125 svn_magic__cookie_t *magic_cookie,
126 apr_pool_t *result_pool,
127 apr_pool_t *scratch_pool)
128 {
129 const char *magic_mimetype = NULL;
130 #ifdef SVN_HAVE_LIBMAGIC
131 apr_finfo_t finfo;
132
133 /* Do not ask libmagic for the mime-types of empty files.
134 * This prevents mime-types like "application/x-empty" from making
135 * Subversion treat empty files as binary. */
136 SVN_ERR(svn_io_stat(&finfo, local_abspath, APR_FINFO_SIZE, scratch_pool));
137 if (finfo.size > 0)
138 {
139 magic_mimetype = magic_file(magic_cookie->magic, local_abspath);
140 if (magic_mimetype)
141 {
142 /* Only return binary mime-types. */
143 if (strncmp(magic_mimetype, "text/", 5) == 0)
144 magic_mimetype = NULL;
145 else
146 {
147 svn_error_t *err;
148 #ifndef MAGIC_MIME_TYPE
149 char *p;
150
151 /* Strip off trailing stuff like " charset=ascii". */
152 p = strchr(magic_mimetype, ' ');
153 if (p)
154 *p = '\0';
155 #endif
156 /* Make sure we got a valid mime type. */
157 err = svn_mime_type_validate(magic_mimetype, scratch_pool);
158 if (err)
159 {
160 if (err->apr_err == SVN_ERR_BAD_MIME_TYPE)
161 {
162 svn_error_clear(err);
163 magic_mimetype = NULL;
164 }
165 else
166 return svn_error_trace(err);
167 }
168 else
169 {
170 /* The string is allocated from memory managed by libmagic
171 * so we must copy it to the result pool. */
172 magic_mimetype = apr_pstrdup(result_pool, magic_mimetype);
173 }
174 }
175 }
176 }
177 #endif
178
179 *mimetype = magic_mimetype;
180 return SVN_NO_ERROR;
181 }
182