1251881Speter/*
2251881Speter * magic.c:  wrappers around libmagic
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter/*** Includes. ***/
28251881Speter
29251881Speter#include <apr_lib.h>
30251881Speter#include <apr_file_info.h>
31251881Speter
32251881Speter#include "svn_io.h"
33251881Speter#include "svn_types.h"
34251881Speter#include "svn_pools.h"
35251881Speter#include "svn_error.h"
36289180Speter#include "svn_config.h"
37289180Speter#include "svn_hash.h"
38251881Speter
39251881Speter#include "svn_private_config.h"
40251881Speter
41251881Speter#include "private/svn_magic.h"
42251881Speter
43251881Speter#ifdef SVN_HAVE_LIBMAGIC
44251881Speter#include <magic.h>
45251881Speter#endif
46251881Speter
47251881Speterstruct svn_magic__cookie_t {
48251881Speter#ifdef SVN_HAVE_LIBMAGIC
49251881Speter  magic_t magic;
50251881Speter#else
51251881Speter  char dummy;
52251881Speter#endif
53251881Speter};
54251881Speter
55251881Speter#ifdef SVN_HAVE_LIBMAGIC
56251881Speter/* Close the magic database. */
57251881Speterstatic apr_status_t
58251881Speterclose_magic_cookie(void *baton)
59251881Speter{
60251881Speter  svn_magic__cookie_t *mc = (svn_magic__cookie_t*)baton;
61251881Speter  magic_close(mc->magic);
62251881Speter  return APR_SUCCESS;
63251881Speter}
64251881Speter#endif
65251881Speter
66289180Spetersvn_error_t *
67251881Spetersvn_magic__init(svn_magic__cookie_t **magic_cookie,
68289180Speter                apr_hash_t *config,
69251881Speter                apr_pool_t *result_pool)
70251881Speter{
71251881Speter  svn_magic__cookie_t *mc = NULL;
72251881Speter
73251881Speter#ifdef SVN_HAVE_LIBMAGIC
74289180Speter  if (config)
75289180Speter    {
76289180Speter      svn_boolean_t enable;
77289180Speter      svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
78289180Speter
79289180Speter      SVN_ERR(svn_config_get_bool(cfg, &enable,
80289180Speter                                  SVN_CONFIG_SECTION_MISCELLANY,
81289180Speter                                  SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE,
82289180Speter                                  TRUE));
83289180Speter      if (!enable)
84289180Speter        {
85289180Speter          *magic_cookie = NULL;
86289180Speter          return SVN_NO_ERROR;
87289180Speter        }
88289180Speter    }
89289180Speter
90251881Speter  mc = apr_palloc(result_pool, sizeof(*mc));
91251881Speter
92251881Speter  /* Initialise libmagic. */
93251881Speter#ifndef MAGIC_MIME_TYPE
94251881Speter  /* Some old versions of libmagic don't support MAGIC_MIME_TYPE.
95251881Speter   * We can use MAGIC_MIME instead. It returns more than we need
96251881Speter   * but we can work around that (see below). */
97251881Speter  mc->magic = magic_open(MAGIC_MIME | MAGIC_ERROR);
98251881Speter#else
99251881Speter  mc->magic = magic_open(MAGIC_MIME_TYPE | MAGIC_ERROR);
100251881Speter#endif
101251881Speter  if (mc->magic)
102251881Speter    {
103251881Speter      /* This loads the default magic database.
104251881Speter       * Point the MAGIC environment variable at your favourite .mgc
105251881Speter       * file to load a non-default database. */
106251881Speter      if (magic_load(mc->magic, NULL) == -1)
107251881Speter        {
108251881Speter          magic_close(mc->magic);
109251881Speter          mc = NULL;
110251881Speter        }
111251881Speter      else
112251881Speter        apr_pool_cleanup_register(result_pool, mc, close_magic_cookie,
113251881Speter                                  apr_pool_cleanup_null);
114251881Speter    }
115251881Speter#endif
116251881Speter
117251881Speter  *magic_cookie = mc;
118289180Speter
119289180Speter  return SVN_NO_ERROR;
120251881Speter}
121251881Speter
122251881Spetersvn_error_t *
123251881Spetersvn_magic__detect_binary_mimetype(const char **mimetype,
124251881Speter                                  const char *local_abspath,
125251881Speter                                  svn_magic__cookie_t *magic_cookie,
126251881Speter                                  apr_pool_t *result_pool,
127251881Speter                                  apr_pool_t *scratch_pool)
128251881Speter{
129251881Speter  const char *magic_mimetype = NULL;
130251881Speter#ifdef SVN_HAVE_LIBMAGIC
131251881Speter  apr_finfo_t finfo;
132251881Speter
133251881Speter  /* Do not ask libmagic for the mime-types of empty files.
134251881Speter   * This prevents mime-types like "application/x-empty" from making
135251881Speter   * Subversion treat empty files as binary. */
136251881Speter  SVN_ERR(svn_io_stat(&finfo, local_abspath, APR_FINFO_SIZE, scratch_pool));
137251881Speter  if (finfo.size > 0)
138251881Speter    {
139251881Speter      magic_mimetype = magic_file(magic_cookie->magic, local_abspath);
140251881Speter      if (magic_mimetype)
141251881Speter        {
142251881Speter          /* Only return binary mime-types. */
143251881Speter          if (strncmp(magic_mimetype, "text/", 5) == 0)
144251881Speter            magic_mimetype = NULL;
145251881Speter          else
146251881Speter            {
147251881Speter              svn_error_t *err;
148251881Speter#ifndef MAGIC_MIME_TYPE
149251881Speter              char *p;
150251881Speter
151251881Speter              /* Strip off trailing stuff like " charset=ascii". */
152251881Speter              p = strchr(magic_mimetype, ' ');
153251881Speter              if (p)
154251881Speter                *p = '\0';
155251881Speter#endif
156251881Speter              /* Make sure we got a valid mime type. */
157251881Speter              err = svn_mime_type_validate(magic_mimetype, scratch_pool);
158251881Speter              if (err)
159251881Speter                {
160251881Speter                  if (err->apr_err == SVN_ERR_BAD_MIME_TYPE)
161251881Speter                    {
162251881Speter                      svn_error_clear(err);
163251881Speter                      magic_mimetype = NULL;
164251881Speter                    }
165251881Speter                  else
166251881Speter                    return svn_error_trace(err);
167251881Speter                }
168251881Speter              else
169251881Speter                {
170251881Speter                  /* The string is allocated from memory managed by libmagic
171251881Speter                   * so we must copy it to the result pool. */
172251881Speter                  magic_mimetype = apr_pstrdup(result_pool, magic_mimetype);
173251881Speter                }
174251881Speter            }
175251881Speter        }
176251881Speter    }
177251881Speter#endif
178251881Speter
179251881Speter  *mimetype = magic_mimetype;
180251881Speter  return SVN_NO_ERROR;
181251881Speter}
182