Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 54 additions & 5 deletions src/typecode/magic2.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

import ctypes
import os
import warnings

from commoncode import command
from plugincode.location_provider import get_location
Expand Down Expand Up @@ -79,18 +80,59 @@
TYPECODE_LIBMAGIC_DATABASE = 'typecode.libmagic.db'


def load_lib_failover():
# loader from python-magic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's nice extra credits but not needed IMHO since Adam is already credited at the top for the whole file.

libmagic = None
# Let's try to find magic or magic1
dll = (ctypes.util.find_library('magic')
or ctypes.util.find_library('magic1')
or ctypes.util.find_library('cygmagic-1')
or ctypes.util.find_library('libmagic-1')
or ctypes.util.find_library('msys-magic-1') # for MSYS2
)
# necessary because find_library returns None if it doesn't find the library
if dll:
libmagic = ctypes.CDLL(dll)

if not libmagic or not libmagic._name:
windows_dlls = ['magic1.dll', 'cygmagic-1.dll', 'libmagic-1.dll', 'msys-magic-1.dll']
platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib',
'/usr/local/lib/libmagic.dylib'] +
# Assumes there will only be one version installed
glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib'), # flake8:noqa
'win32': windows_dlls,
'cygwin': windows_dlls,
'linux': ['libmagic.so.1'],
# fallback for some Linuxes (e.g. Alpine) where library search does not work # flake8:noqa
}
platform = 'linux' if sys.platform.startswith('linux') else sys.platform
for dll in platform_to_lib.get(platform, []):
try:
libmagic = ctypes.CDLL(dll)
break
except OSError:
pass

if not libmagic or not libmagic._name:
return None
return libmagic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if there could be a way to test/check the version of this libmagic? Because unless this is close enough to the one provided as a plugin, there could be some magic database inconsistencies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do a check, but we don't really know what threshold we should really put there. How would you define close enough?
I would suggest to leave a warning, and let the community report any issue.

We could have a TAINTED flag like linux does which could be put in the typecode dependent reports

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well in hindsight, let's leave it this way.... after all in https://github.com/nexB/scancode-plugins/blob/main/builtins/typecode_libmagic_system_provided/src/typecode_libmagic/__init__.py I do not even check for version


def load_lib():
"""
Return the loaded libmagic shared library object from plugin-provided path.
"""
dll = get_location(TYPECODE_LIBMAGIC_DLL)
libdir = get_location(TYPECODE_LIBMAGIC_LIBDIR)
if not (dll and libdir) or not os.path.isfile(dll) or not os.path.isdir(libdir):
raise Exception(
'CRITICAL: libmagic DLL and is magic database are not installed. '
'Unable to continue: you need to install a valid typecode-libmagic '
'plugin with a valid and proper libmagic and magic DB available.'
)
ret = load_lib_failover()
if ret is None:
raise ImportError(
'CRITICAL: libmagic DLL and is magic database are not installed. '
'Unable to continue: you need to install a valid typecode-libmagic '
'plugin with a valid and proper libmagic and magic DB available.'
)
warnings.warn("System libmagic is used. Install typecode-libmagic for best consitency")
return ret
return command.load_shared_library(dll, libdir)


Expand Down Expand Up @@ -164,6 +206,7 @@ def __init__(self, flags, magic_db_location=None):
self.flags = flags
self.cookie = _magic_open(self.flags)
if not magic_db_location:
# if no plugin, None is returned, and libmagic will load the default db
magic_db_location = get_location(TYPECODE_LIBMAGIC_DATABASE)

# Note: this location must always be bytes on Python2 and 3, all OSes
Expand Down Expand Up @@ -257,3 +300,9 @@ def check_error(result, func, args): # NOQA
_magic_load.restype = ctypes.c_int
_magic_load.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
_magic_load.errcheck = check_error

_magic_version = libmagic.magic_version
_magic_version.restype = ctypes.c_int
_magic_version.argtypes = []

libmagic_version = _magic_version()
27 changes: 27 additions & 0 deletions tests/test_libmagic_load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#
# Copyright (c) nexB Inc. and others.
# SPDX-License-Identifier: Apache-2.0
#
# Visit https://aboutcode.org and https://github.com/nexB/ for support and download.
# ScanCode is a trademark of nexB Inc.
#
# Licensed 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.
#

import os

from typecode.magic2 import libmagic_version


def test_load_lib():
assert libmagic_version > 0