The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * 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_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;
#endif
  char dummy;
};

#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

void
svn_magic__init(svn_magic__cookie_t **magic_cookie,
                apr_pool_t *result_pool)
{

  svn_magic__cookie_t *mc = NULL;

#ifdef SVN_HAVE_LIBMAGIC
  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;
}

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;
}