dependencies.py 9.82 KB
Newer Older
1
# -*- coding: utf-8 -*-
2
"""Functionality to check for the availability and version of dependencies.
3

4 5 6
This file is generated by l2tdevtools update-dependencies.py, any dependency
related changes should be made in dependencies.ini.
"""
7

8 9
from __future__ import print_function
from __future__ import unicode_literals
10

11
import re
12 13


14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
# Dictionary that contains version tuples per module name.
#
# A version tuple consists of:
# (version_attribute_name, minimum_version, maximum_version, is_required)
#
# Where version_attribute_name is either a name of an attribute,
# property or method.
PYTHON_DEPENDENCIES = {
    'artifacts': ('__version__', '20170818', None, True),
    'bencode': ('', '', None, True),
    'biplist': ('', '1.0.3', None, True),
    'certifi': ('__version__', '2016.9.26', None, True),
    'chardet': ('__version__', '2.0.1', None, True),
    'Crypto': ('__version__', '2.6', None, True),
    'dateutil': ('__version__', '1.5', None, True),
    'dfdatetime': ('__version__', '20180704', None, True),
    'dfvfs': ('__version__', '20181209', None, True),
    'dfwinreg': ('__version__', '20180712', None, True),
    'dtfabric': ('__version__', '20181128', None, True),
    'efilter': ('', '1.5', None, True),
    'elasticsearch': ('__versionstr__', '6.0', None, False),
    'elasticsearch5': ('__versionstr__', '5.4.0', None, True),
    'future': ('__version__', '0.16.0', None, True),
    'idna': ('', '2.5', None, True),
    'lz4': ('', '0.10.0', None, False),
    'lzma': ('__version__', '', None, False),
    'pefile': ('__version__', '2018.8.8', None, True),
    'psutil': ('__version__', '5.4.3', None, True),
    'pybde': ('get_version()', '20140531', None, True),
    'pyesedb': ('get_version()', '20150409', None, True),
    'pyevt': ('get_version()', '20120410', None, True),
    'pyevtx': ('get_version()', '20141112', None, True),
    'pyewf': ('get_version()', '20131210', None, True),
    'pyfsapfs': ('get_version()', '20181205', None, True),
    'pyfsntfs': ('get_version()', '20151130', None, True),
    'pyfvde': ('get_version()', '20160719', None, True),
    'pyfwnt': ('get_version()', '20180117', None, True),
    'pyfwsi': ('get_version()', '20150606', None, True),
    'pylnk': ('get_version()', '20150830', None, True),
    'pymsiecf': ('get_version()', '20150314', None, True),
    'pyolecf': ('get_version()', '20151223', None, True),
55
    'pyparsing': ('__version__', '2.3.0', None, True),
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
    'pyqcow': ('get_version()', '20131204', None, True),
    'pyregf': ('get_version()', '20150315', None, True),
    'pyscca': ('get_version()', '20161031', None, True),
    'pysigscan': ('get_version()', '20150627', None, True),
    'pysmdev': ('get_version()', '20140529', None, True),
    'pysmraw': ('get_version()', '20140612', None, True),
    'pytsk3': ('get_version()', '20160721', None, True),
    'pytz': ('__version__', '', None, True),
    'pyvhdi': ('get_version()', '20131210', None, True),
    'pyvmdk': ('get_version()', '20140421', None, True),
    'pyvshadow': ('get_version()', '20160109', None, True),
    'pyvslvm': ('get_version()', '20160109', None, True),
    'requests': ('__version__', '2.18.0', None, True),
    'six': ('__version__', '1.1.0', None, True),
    'sqlite3': ('__version__', '', None, True),
    'urllib3': ('__version__', '1.21.1', None, True),
    'xlsxwriter': ('__version__', '0.9.3', None, True),
    'yaml': ('__version__', '3.10', None, True),
    'yara': ('YARA_VERSION', '3.4.0', None, True),
    'zmq': ('__version__', '2.1.11', None, True)}
76

77
_VERSION_SPLIT_REGEX = re.compile(r'\.|\-')
78 79 80 81


def _CheckPythonModule(
    module_name, version_attribute_name, minimum_version,
82
    is_required=True, maximum_version=None, verbose_output=True):
83 84 85
  """Checks the availability of a Python module.

  Args:
86 87
    module_name (str): name of the module.
    version_attribute_name (str): name of the attribute that contains
88
       the module version or method to retrieve the module version.
89
    minimum_version (str): minimum required version.
90 91
    is_required (Optional[bool]): True if the Python module is a required
        dependency.
92 93 94
    maximum_version (Optional[str]): maximum required version. Should only be
        used if there is a later version that is not supported.
    verbose_output (Optional[bool]): True if output should be verbose.
95 96

  Returns:
97 98
    bool: True if the Python module is available and conforms to
        the minimum required version, False otherwise.
99
  """
100 101
  module_object = _ImportPythonModule(module_name)
  if not module_object:
102 103 104 105 106
    if not is_required:
      print('[OPTIONAL]\tmissing: {0:s}.'.format(module_name))
      return True

    print('[FAILURE]\tmissing: {0:s}.'.format(module_name))
107 108
    return False

109 110
  if not version_attribute_name or not minimum_version:
    if verbose_output:
111
      print('[OK]\t\t{0:s}'.format(module_name))
112
    return True
113

114
  module_version = None
115
  if not version_attribute_name.endswith('()'):
116 117 118 119 120 121
    module_version = getattr(module_object, version_attribute_name, None)
  else:
    version_method = getattr(module_object, version_attribute_name[:-2], None)
    if version_method:
      module_version = version_method()

122
  if not module_version:
123 124 125 126 127 128
    if not is_required:
      print((
          '[OPTIONAL]\tunable to determine version information '
          'for: {0:s}').format(module_name))
      return True

129
    print((
130 131
        '[FAILURE]\tunable to determine version information '
        'for: {0:s}').format(module_name))
132
    return False
133

134 135 136
  # Make sure the module version is a string.
  module_version = '{0!s}'.format(module_version)

137 138
  # Split the version string and convert every digit into an integer.
  # A string compare of both version strings will yield an incorrect result.
139 140 141 142
  module_version_map = list(
      map(int, _VERSION_SPLIT_REGEX.split(module_version)))
  minimum_version_map = list(
      map(int, _VERSION_SPLIT_REGEX.split(minimum_version)))
143 144

  if module_version_map < minimum_version_map:
145 146 147 148 149 150
    if not is_required:
      print((
          '[OPTIONAL]\t{0:s} version: {1!s} is too old, {2!s} or later '
          'required.').format(module_name, module_version, minimum_version))
      return True

151
    print((
152 153
        '[FAILURE]\t{0:s} version: {1!s} is too old, {2!s} or later '
        'required.').format(module_name, module_version, minimum_version))
154
    return False
155

156
  if maximum_version:
157 158
    maximum_version_map = list(
        map(int, _VERSION_SPLIT_REGEX.split(maximum_version)))
159
    if module_version_map > maximum_version_map:
160 161 162 163 164 165
      if not is_required:
        print((
            '[OPTIONAL]\t{0:s} version: {1!s} is too recent, {2!s} or earlier '
            'required.').format(module_name, module_version, minimum_version))
        return True

166
      print((
167 168
          '[FAILURE]\t{0:s} version: {1!s} is too recent, {2!s} or earlier '
          'required.').format(module_name, module_version, maximum_version))
169 170
      return False

171
  if verbose_output:
172
    print('[OK]\t\t{0:s} version: {1!s}'.format(module_name, module_version))
173 174 175 176

  return True


177
def _CheckSQLite3(verbose_output=True):
178 179 180
  """Checks the availability of sqlite3.

  Args:
181
    verbose_output (Optional[bool]): True if output should be verbose.
182 183

  Returns:
184
    bool: True if the sqlite3 Python module is available, False otherwise.
185 186 187 188 189 190
  """
  # On Windows sqlite3 can be provided by both pysqlite2.dbapi2 and
  # sqlite3. sqlite3 is provided with the Python installation and
  # pysqlite2.dbapi2 by the pysqlite2 Python module. Typically
  # pysqlite2.dbapi2 would contain a newer version of sqlite3, hence
  # we check for its presence first.
191 192
  module_name = 'pysqlite2.dbapi2'
  minimum_version = '3.7.8'
193 194 195

  module_object = _ImportPythonModule(module_name)
  if not module_object:
196
    module_name = 'sqlite3'
197 198 199

  module_object = _ImportPythonModule(module_name)
  if not module_object:
200
    print('[FAILURE]\tmissing: {0:s}.'.format(module_name))
201 202
    return False

203
  module_version = getattr(module_object, 'sqlite_version', None)
204 205 206 207 208
  if not module_version:
    return False

  # Split the version string and convert every digit into an integer.
  # A string compare of both version strings will yield an incorrect result.
209 210 211 212
  module_version_map = list(
      map(int, _VERSION_SPLIT_REGEX.split(module_version)))
  minimum_version_map = list(
      map(int, _VERSION_SPLIT_REGEX.split(minimum_version)))
213 214 215

  if module_version_map < minimum_version_map:
    print((
216 217
        '[FAILURE]\t{0:s} version: {1!s} is too old, {2!s} or later '
        'required.').format(module_name, module_version, minimum_version))
218 219 220
    return False

  if verbose_output:
221
    print('[OK]\t\t{0:s} version: {1!s}'.format(module_name, module_version))
222 223 224 225

  return True


226 227 228 229 230 231 232 233 234 235 236 237
def _ImportPythonModule(module_name):
  """Imports a Python module.

  Args:
    module_name (str): name of the module.

  Returns:
    module: Python module or None if the module cannot be imported.
  """
  try:
    module_object = list(map(__import__, [module_name]))[0]
  except ImportError:
238
    return None
239 240

  # If the module name contains dots get the upper most module object.
241 242
  if '.' in module_name:
    for submodule_name in module_name.split('.')[1:]:
243 244 245 246 247
      module_object = getattr(module_object, submodule_name, None)

  return module_object


248
def CheckDependencies(verbose_output=True):
249 250 251
  """Checks the availability of the dependencies.

  Args:
252
    verbose_output (Optional[bool]): True if output should be verbose.
253 254

  Returns:
255
    bool: True if the dependencies are available, False otherwise.
256
  """
257
  print('Checking availability and versions of dependencies.')
258 259
  check_result = True

260
  for module_name, version_tuple in sorted(PYTHON_DEPENDENCIES.items()):
261
    if not _CheckPythonModule(
262 263
        module_name, version_tuple[0], version_tuple[1],
        is_required=version_tuple[3], maximum_version=version_tuple[2],
264
        verbose_output=verbose_output):
265 266
      check_result = False

267
  if not _CheckSQLite3(verbose_output=verbose_output):
268 269 270
    check_result = False

  if check_result and not verbose_output:
271
    print('[OK]')
272

273
  print('')
274
  return check_result