/*
* magic.c: wrappers around libmagic
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
/* ==================================================================== */
/*** Includes. ***/
#include <apr_lib.h>
#include <apr_file_info.h>
#include "svn_io.h"
#include "svn_types.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_config.h"
#include "svn_hash.h"
#include "svn_private_config.h"
#include "private/svn_magic.h"
#ifdef SVN_HAVE_LIBMAGIC
#include <magic.h>
#endif
struct svn_magic__cookie_t {
#ifdef SVN_HAVE_LIBMAGIC
magic_t magic;
#else
char dummy;
#endif
};
#ifdef SVN_HAVE_LIBMAGIC
/* Close the magic database. */
static apr_status_t
close_magic_cookie(void *baton)
{
svn_magic__cookie_t *mc = (svn_magic__cookie_t*)baton;
magic_close(mc->magic);
return APR_SUCCESS;
}
#endif
svn_error_t *
svn_magic__init(svn_magic__cookie_t **magic_cookie,
apr_hash_t *config,
apr_pool_t *result_pool)
{
svn_magic__cookie_t *mc = NULL;
#ifdef SVN_HAVE_LIBMAGIC
if (config)
{
svn_boolean_t enable;
svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
SVN_ERR(svn_config_get_bool(cfg, &enable,
SVN_CONFIG_SECTION_MISCELLANY,
SVN_CONFIG_OPTION_ENABLE_MAGIC_FILE,
TRUE));
if (!enable)
{
*magic_cookie = NULL;
return SVN_NO_ERROR;
}
}
mc = apr_palloc(result_pool, sizeof(*mc));
/* Initialise libmagic. */
#ifndef MAGIC_MIME_TYPE
/* Some old versions of libmagic don't support MAGIC_MIME_TYPE.
* We can use MAGIC_MIME instead. It returns more than we need
* but we can work around that (see below). */
mc->magic = magic_open(MAGIC_MIME | MAGIC_ERROR);
#else
mc->magic = magic_open(MAGIC_MIME_TYPE | MAGIC_ERROR);
#endif
if (mc->magic)
{
/* This loads the default magic database.
* Point the MAGIC environment variable at your favourite .mgc
* file to load a non-default database. */
if (magic_load(mc->magic, NULL) == -1)
{
magic_close(mc->magic);
mc = NULL;
}
else
apr_pool_cleanup_register(result_pool, mc, close_magic_cookie,
apr_pool_cleanup_null);
}
#endif
*magic_cookie = mc;
return SVN_NO_ERROR;
}
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)
{
const char *magic_mimetype = NULL;
#ifdef SVN_HAVE_LIBMAGIC
apr_finfo_t finfo;
/* Do not ask libmagic for the mime-types of empty files.
* This prevents mime-types like "application/x-empty" from making
* Subversion treat empty files as binary. */
SVN_ERR(svn_io_stat(&finfo, local_abspath, APR_FINFO_SIZE, scratch_pool));
if (finfo.size > 0)
{
magic_mimetype = magic_file(magic_cookie->magic, local_abspath);
if (magic_mimetype)
{
/* Only return binary mime-types. */
if (strncmp(magic_mimetype, "text/", 5) == 0)
magic_mimetype = NULL;
else
{
svn_error_t *err;
#ifndef MAGIC_MIME_TYPE
char *p;
/* Strip off trailing stuff like " charset=ascii". */
p = strchr(magic_mimetype, ' ');
if (p)
*p = '\0';
#endif
/* Make sure we got a valid mime type. */
err = svn_mime_type_validate(magic_mimetype, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_BAD_MIME_TYPE)
{
svn_error_clear(err);
magic_mimetype = NULL;
}
else
return svn_error_trace(err);
}
else
{
/* The string is allocated from memory managed by libmagic
* so we must copy it to the result pool. */
magic_mimetype = apr_pstrdup(result_pool, magic_mimetype);
}
}
}
}
#endif
*mimetype = magic_mimetype;
return SVN_NO_ERROR;
}