diff --git a/lib/galaxy/tool_shed/tool_dependencies/common_util.py b/lib/galaxy/tool_shed/tool_dependencies/common_util.py index 726d9e012e9..bd49f9b18b0 100644 --- a/lib/galaxy/tool_shed/tool_dependencies/common_util.py +++ b/lib/galaxy/tool_shed/tool_dependencies/common_util.py @@ -1,12 +1,24 @@ -import os, shutil, tarfile, urllib2 +import os, shutil, tarfile, urllib2, zipfile from galaxy.datatypes.checkers import * +def zipfile_ok( path_to_archive ): + """ + This function is a bit pedantic and not functionally necessary. It checks whether there is no file pointing outside of the extraction, + because ZipFile.extractall() has some potential security holes. See python zipfile documentation for more details. + """ + basename = os.path.realpath( os.path.dirname( path_to_archive ) ) + zip_archive = zipfile.ZipFile( path_to_archive ) + for member in zip_archive.namelist(): + member_path = os.path.realpath( os.path.join( basename, member ) ) + if not member_path.startswith( basename ): + return False + return True def create_env_var_dict( elem, tool_dependency_install_dir=None, tool_shed_repository_install_dir=None ): env_var_name = elem.get( 'name', 'PATH' ) env_var_action = elem.get( 'action', 'prepend_to' ) env_var_text = None if elem.text and elem.text.find( 'REPOSITORY_INSTALL_DIR' ) >= 0: - if tool_shed_repository_install_dir: + if tool_shed_repository_install_dir and elem.text.find( '$REPOSITORY_INSTALL_DIR' ) != -1: env_var_text = elem.text.replace( '$REPOSITORY_INSTALL_DIR', tool_shed_repository_install_dir ) return dict( name=env_var_name, action=env_var_action, value=env_var_text ) else: @@ -50,6 +62,20 @@ def extract_tar( file_name, file_path ): tar = tarfile.open( file_name ) tar.extractall( path=file_path ) tar.close() +def extract_zip( archive_path, extraction_path ): + # TODO: change this method to use zipfile.Zipfile.extractall() when we stop supporting Python 2.5. + if not zipfile_ok( archive_path ): + return False + zip_archive = zipfile.ZipFile( archive_path, 'r' ) + for name in zip_archive.namelist(): + uncompressed_path = os.path.join( extraction_path, name ) + if uncompressed_path.endswith( '/' ): + if not os.path.isdir( uncompressed_path ): + os.makedirs( uncompressed_path ) + else: + file( uncompressed_path, 'wb' ).write( zip_archive.read( name ) ) + zip_archive.close() + return True def isbz2( file_path ): return is_bz2( file_path ) def isgzip( file_path ): @@ -102,3 +128,13 @@ def url_download( install_dir, downloaded_file_name, download_url ): if dst: dst.close() return os.path.abspath( file_path ) +def zip_extraction_directory( file_path, file_name ): + """Try to return the correct extraction directory.""" + files = [ filename for filename in os.listdir( file_path ) if not filename.endswith( '.zip' ) ] + if len( files ) > 1: + return os.path.abspath( file_path ) + elif len( files ) == 1: + # If there is only on file it should be a directory. + if os.path.isdir( os.path.join( file_path, files[ 0 ] ) ): + return os.path.abspath( os.path.join( file_path, files[ 0 ] ) ) + raise ValueError( 'Could not find directory for the extracted file %s' % os.path.abspath( os.path.join( file_path, file_name ) ) ) diff --git a/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py b/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py index 8a09c96a180..5103af2a2bb 100644 --- a/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py +++ b/lib/galaxy/tool_shed/tool_dependencies/fabric_util.py @@ -49,8 +49,6 @@ def install_and_build_package( app, tool_dependency, actions_dict ): sa_session = app.model.context.current install_dir = actions_dict[ 'install_dir' ] package_name = actions_dict[ 'package_name' ] - #download_url = actions_dict.get( 'download_url', None ) - #clone_cmd = actions_dict.get( 'clone_cmd', None ) actions = actions_dict.get( 'actions', None ) if actions: with make_tmp_dir() as work_dir: @@ -59,13 +57,17 @@ def install_and_build_package( app, tool_dependency, actions_dict ): # are currently only two supported processes; download_by_url and clone via a "shell_command" action type. action_type, action_dict = actions[ 0 ] if action_type == 'download_by_url': - # http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1.18.tar.bz2 url = action_dict[ 'url' ] downloaded_filename = os.path.split( url )[ -1 ] downloaded_file_path = common_util.url_download( work_dir, downloaded_filename, url ) if common_util.istar( downloaded_file_path ): + # http://sourceforge.net/projects/samtools/files/samtools/0.1.18/samtools-0.1.18.tar.bz2 common_util.extract_tar( downloaded_file_path, work_dir ) dir = common_util.tar_extraction_directory( work_dir, downloaded_filename ) + elif common_util.iszip( downloaded_file_path ): + # http://downloads.sourceforge.net/project/picard/picard-tools/1.56/picard-tools-1.56.zip + zip_archive_extracted = common_util.extract_zip( downloaded_file_path, work_dir ) + dir = common_util.zip_extraction_directory( work_dir, downloaded_filename ) else: dir = work_dir elif action_type == 'shell_command': diff --git a/templates/admin/tool_shed_repository/browse_tool_dependency.mako b/templates/admin/tool_shed_repository/browse_tool_dependency.mako index f3a170bcdc7..601a768b8c1 100644 --- a/templates/admin/tool_shed_repository/browse_tool_dependency.mako +++ b/templates/admin/tool_shed_repository/browse_tool_dependency.mako @@ -58,7 +58,7 @@ ${tool_dependency.status}
- %if repository.in_error_state: + %if tool_dependency.in_error_state:
${tool_dependency.error_message}