#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2012 The Plaso Project Authors. # Please see the AUTHORS file for details on individual authors. # # 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. """Script to check for the availability and version of dependencies.""" import re import urllib2 def DownloadPageContent(download_url): """Downloads the page content. Args: download_url: the URL where to download the page content. Returns: The page content if successful, None otherwise. """ if not download_url: return url_object = urllib2.urlopen(download_url) if url_object.code != 200: return return url_object.read() def GetLibyalGithubReleasesLatestVersion(library_name): """Retrieves the latest version number of a libyal library on GitHub releases. Args: library_name: the name of the libyal library. Returns: The latest version for a given libyal library on GitHub releases or 0 on error. """ download_url = ( u'https://github.com/libyal/{0:s}/releases').format(library_name) page_content = DownloadPageContent(download_url) if not page_content: return 0 # The format of the project download URL is: # /libyal/{project name}/releases/download/{git tag}/ # {project name}{status-}{version}.tar.gz # Note that the status is optional and will be: beta, alpha or experimental. expression_string = ( u'/libyal/{0:s}/releases/download/[^/]*/{0:s}-[a-z-]*([0-9]+)' u'[.]tar[.]gz').format(library_name) matches = re.findall(expression_string, page_content) if not matches: return 0 return int(max(matches)) # TODO: Remove when Google Drive support is no longer needed. def GetLibyalGoogleDriveLatestVersion(library_name): """Retrieves the latest version number of a libyal library on Google Drive. Args: library_name: the name of the libyal library. Returns: The latest version for a given libyal library on Google Drive or 0 on error. """ download_url = 'https://code.google.com/p/{0:s}/'.format(library_name) page_content = DownloadPageContent(download_url) if not page_content: return 0 # The format of the library downloads URL is: # https://googledrive.com/host/{random string}/ expression_string = ( ']*>Downloads') matches = re.findall(expression_string, page_content) if not matches or len(matches) != 1: return 0 page_content = DownloadPageContent(matches[0]) if not page_content: return 0 # The format of the library download URL is: # /host/{random string}/{library name}-{status-}{version}.tar.gz # Note that the status is optional and will be: beta, alpha or experimental. expression_string = '/host/[^/]*/{0:s}-[a-z-]*([0-9]+)[.]tar[.]gz'.format( library_name) matches = re.findall(expression_string, page_content) if not matches: return 0 return int(max(matches)) def CheckLibyal(libyal_python_modules): """Checks the availability of libyal libraries. Args: libyal_python_modules: list of libyal python module names. Returns: True if the libyal libraries are available, false otherwise. """ connection_error = False result = True for module_name, module_version in libyal_python_modules: try: module_object = map(__import__, [module_name])[0] module_loaded = True except ImportError: print u'[FAILURE]\tmissing: {0:s}.'.format(module_name) module_loaded = False result = False if module_loaded: libyal_name = u'lib{0:s}'.format(module_name[2:]) installed_version = int(module_object.get_version()) try: latest_version = GetLibyalGithubReleasesLatestVersion(libyal_name) except urllib2.URLError: latest_version = 0 if not latest_version: try: latest_version = GetLibyalGoogleDriveLatestVersion(libyal_name) except urllib2.URLError: latest_version = 0 if not latest_version: print ( u'Unable to determine latest version of {0:s} ({1:s}).\n').format( libyal_name, module_name) latest_version = None connection_error = True if module_version is not None and installed_version < module_version: print ( u'[FAILURE]\t{0:s} ({1:s}) version: {2:d} is too old, {3:d} or ' u'later required.').format( libyal_name, module_name, installed_version, module_version) result = False elif latest_version and installed_version != latest_version: print ( u'[INFO]\t\t{0:s} ({1:s}) version: {2:d} installed, ' u'version: {3:d} available.').format( libyal_name, module_name, installed_version, latest_version) else: print u'[OK]\t\t{0:s} ({1:s}) version: {2:d}'.format( libyal_name, module_name, installed_version) if connection_error: print ( u'[INFO] to check for the latest versions this script needs Internet ' u'access.') return result def CheckPythonModule( module_name, version_attribute_name, minimum_version, maximum_version=None): """Checks the availability of a Python module. Args: module_name: the name of the module. version_attribute_name: the name of the attribute that contains the module version. minimum_version: the minimum required version. maximum_version: the maximum required version. This attribute is optional and should only be used if there is a recent API change that prevents the tool from running if a later version is used. Returns: True if the Python module is available and conforms to the minimum required version. False otherwise. """ try: module_object = map(__import__, [module_name])[0] except ImportError: print u'[FAILURE]\tmissing: {0:s}.'.format(module_name) return False if version_attribute_name and minimum_version: module_version = getattr(module_object, version_attribute_name, None) 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. module_version_map = map(int, module_version.split('.')) minimum_version_map = map(int, minimum_version.split('.')) if module_version_map < minimum_version_map: print ( u'[FAILURE]\t{0:s} version: {1:s} is too old, {2:s} or later ' u'required.').format(module_name, module_version, minimum_version) return False if maximum_version: maximum_version_map = map(int, maximum_version.split('.')) if module_version_map > maximum_version_map: print ( u'[FAILURE]\t{0:s} version: {1:s} is too recent, {2:s} or earlier ' u'required.').format(module_name, module_version, maximum_version) return False print u'[OK]\t\t{0:s} version: {1:s}'.format(module_name, module_version) else: print u'[OK]\t\t{0:s}'.format(module_name) return True def CheckPytsk(): """Checks the availability of pytsk3. Returns: True if the pytsk3 Python module is available, false otherwise. """ module_name = 'pytsk3' try: module_object = map(__import__, [module_name])[0] except ImportError: print u'[FAILURE]\tmissing: {0:s}.'.format(module_name) return False minimum_version = '4.1.2' module_version = module_object.TSK_VERSION_STR # Split the version string and convert every digit into an integer. # A string compare of both version strings will yield an incorrect result. module_version_map = map(int, module_version.split('.')) minimum_version_map = map(int, minimum_version.split('.')) if module_version_map < minimum_version_map: print ( u'[FAILURE]\tSleuthKit version: {0:s} is too old, {1:s} or later ' u'required.').format(module_version, minimum_version) return False print u'[OK]\t\tSleuthKit version: {0:s}'.format(module_version) minimum_version = '20140506' if not hasattr(module_object, 'get_version'): print u'[FAILURE]\t{0:s} is too old, {1:s} or later required.'.format( module_name, minimum_version) return False module_version = module_object.get_version() if module_version < minimum_version: print ( u'[FAILURE]\t{0:s} version: {1:s} is too old, {2:s} or later ' u'required.').format(module_name, module_version, minimum_version) return False print u'[OK]\t\t{0:s} version: {1:s}'.format(module_name, module_version) return True if __name__ == '__main__': check_result = True print u'Checking availability and versions of plaso dependencies.' # The bencode module does not appear to have no version information. if not CheckPythonModule('bencode', '', ''): check_result = False if not CheckPythonModule('binplist', '__version__', '0.1.4'): check_result = False if not CheckPythonModule('construct', '__version__', '2.5.2'): check_result = False if not CheckPythonModule('dateutil.parser', '', ''): check_result = False if not CheckPythonModule('dfvfs', '__version__', '20141220'): check_result = False if not CheckPythonModule('dpkt', '__version__', '1.8'): check_result = False # The protobuf module does not appear to have version information. if not CheckPythonModule('google.protobuf', '', ''): check_result = False if not CheckPythonModule('hachoir_core', '__version__', '1.3.3'): check_result = False if not CheckPythonModule('hachoir_parser', '__version__', '1.3.4'): check_result = False if not CheckPythonModule('hachoir_metadata', '__version__', '1.3.3'): check_result = False if not CheckPythonModule('IPython', '__version__', '1.2.1'): check_result = False if not CheckPythonModule('yaml', '__version__', '3.10'): check_result = False if not CheckPythonModule('psutil', '__version__', '1.2.1'): check_result = False if not CheckPythonModule('pyparsing', '__version__', '2.0.2'): check_result = False # TODO: determine the version of pytz. # pytz uses __version__ but has a different version indicator e.g. 2012d if not CheckPythonModule('pytz', '', ''): check_result = False if not CheckPythonModule('six', '__version__', '1.1.0'): check_result = False if not CheckPythonModule('sqlite3', 'sqlite_version', '3.7.8'): check_result = False if not CheckPytsk(): check_result = False libyal_check_result = CheckLibyal([ ('pybde', 20140531), ('pyesedb', 20140301), ('pyevt', None), ('pyevtx', 20141112), ('pyewf', 20131210), ('pyfwsi', 20140714), ('pylnk', 20141026), ('pymsiecf', 20130317), ('pyolecf', 20131012), ('pyqcow', 20131204), ('pyregf', 20130716), ('pysmdev', 20140529), ('pysmraw', 20140612), ('pyvhdi', 20131210), ('pyvmdk', 20140421), ('pyvshadow', 20131209), ]) if not check_result: build_instructions_url = ( u'https://sites.google.com/a/kiddaland.net/plaso/developer' u'/building-the-tool') print u'See: {0:s} on how to set up plaso.'.format( build_instructions_url) if not libyal_check_result: libyal_downloads_url = ( u'https://googledrive.com/host/0B30H7z4S52FleW5vUHBnblJfcjg' u'/libyal.html') print u'Libyal libraries can be downloaded from here: {0:s}'.format( libyal_downloads_url) print u''