Merge pull request #93 from Swich1987/master

Added password support for all archivers
This commit is contained in:
Yaroslav Halchenko 2021-02-18 09:32:54 -05:00 committed by GitHub
commit 723006abd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 401 additions and 119 deletions

View File

@ -30,6 +30,9 @@ Examples
# Extract several archives with different formats
patool extract archive.zip otherarchive.rar
# Extract archive with password
patool extract --password somepassword archive.rar
# Test archive integrity
patool test --verbose dist.tar.gz
@ -39,6 +42,9 @@ patool list package.deb
# Create a new archive
patool create --verbose /path/to/myfiles.zip file1.txt dir/
# Create a new archive with password
patool create --verbose --password somepassword /path/to/myfiles.zip file1.txt dir/
# Show differences between two archives
patool diff release1.0.tar.gz release2.0.zip

18
patool
View File

@ -30,7 +30,7 @@ def run_extract(args):
res = 0
for archive in args.archive:
try:
patoolib.extract_archive(archive, verbosity=args.verbosity, interactive=args.interactive, outdir=args.outdir)
patoolib.extract_archive(archive, verbosity=args.verbosity, interactive=args.interactive, outdir=args.outdir, password=args.password)
except PatoolError as msg:
log_error("error extracting %s: %s" % (archive, msg))
res += 1
@ -44,7 +44,7 @@ def run_list(args):
try:
# increase default verbosity since the listing output should be visible
verbosity = args.verbosity + 1
patoolib.list_archive(archive, verbosity=verbosity, interactive=args.interactive)
patoolib.list_archive(archive, verbosity=verbosity, interactive=args.interactive, password=args.password)
except PatoolError as msg:
log_error("error listing %s: %s" % (archive, msg))
res += 1
@ -56,7 +56,7 @@ def run_test(args):
res = 0
for archive in args.archive:
try:
patoolib.test_archive(archive, verbosity=args.verbosity, interactive=args.interactive)
patoolib.test_archive(archive, verbosity=args.verbosity, interactive=args.interactive, password=args.password)
except PatoolError as msg:
log_error("error testing %s: %s" % (archive, msg))
res += 1
@ -67,7 +67,7 @@ def run_create(args):
"""Create an archive from given files."""
res = 0
try:
patoolib.create_archive(args.archive, args.filename, verbosity=args.verbosity, interactive=args.interactive)
patoolib.create_archive(args.archive, args.filename, verbosity=args.verbosity, interactive=args.interactive, password=args.password)
except PatoolError as msg:
log_error("error creating %s: %s" % (args.archive, msg))
res = 1
@ -87,7 +87,7 @@ def run_diff(args):
def run_search(args):
"""Search for pattern in given archive."""
try:
res = patoolib.search_archive(args.pattern, args.archive, verbosity=args.verbosity, interactive=args.interactive)
res = patoolib.search_archive(args.pattern, args.archive, verbosity=args.verbosity, interactive=args.interactive, password=args.password)
except PatoolError as msg:
log_error("error searching %s: %s" % (args.archive, msg))
res = 2
@ -109,7 +109,7 @@ def run_recompress(args):
"""Recompress an archive to smaller size."""
res = 0
try:
patoolib.recompress_archive(args.archive, verbosity=args.verbosity, interactive=args.interactive)
patoolib.recompress_archive(args.archive, verbosity=args.verbosity, interactive=args.interactive, password=args.password)
except PatoolError as msg:
log_error("error recompressing %s: %s" % (args.archive, msg))
res = 1
@ -164,16 +164,20 @@ def create_argparser():
# extract
parser_extract = subparsers.add_parser('extract', help='extract one or more archives')
parser_extract.add_argument('--outdir', help="output directory to extract to")
parser_extract.add_argument('--password', help="password for encrypted files")
parser_extract.add_argument('archive', nargs='+', help="an archive file")
# list
parser_list = subparsers.add_parser('list', help='list members or one or more archives')
parser_list.add_argument('--password', help="password for encrypted files")
parser_list.add_argument('archive', nargs='+', help="an archive file")
# create
parser_create = subparsers.add_parser('create', help='create an archive')
parser_create.add_argument('--password', help="password to encrypt files")
parser_create.add_argument('archive', help="the archive file; the file extension determines the archive program")
parser_create.add_argument('filename', nargs='+', help="a file or directory to add to the archive; note that some archive programs do not support directories")
# test
parser_test = subparsers.add_parser('test', help='test an archive')
parser_test.add_argument('--password', help="password for encrypted files")
parser_test.add_argument('archive', nargs='+', help='an archive file')
# repack
parser_repack = subparsers.add_parser('repack', help='repack an archive to a different format')
@ -181,6 +185,7 @@ def create_argparser():
parser_repack.add_argument('archive_dst', help='target archive file')
# recompress
parser_recompress = subparsers.add_parser('recompress', help='recompress an archive to smaller size')
parser_recompress.add_argument('--password', help="password for encrypted files")
parser_recompress.add_argument('archive', help='an archive file')
# diff
parser_diff = subparsers.add_parser('diff', help='show differences between two archives')
@ -188,6 +193,7 @@ def create_argparser():
parser_diff.add_argument('archive2', help='the second archive file')
# search
parser_search = subparsers.add_parser('search', help="search contents of archive members")
parser_search.add_argument('--password', help="password for encrypted files")
parser_search.add_argument('pattern', help='the grep(1) compatible search pattern')
parser_search.add_argument('archive', help='the archive file')
# formats

View File

@ -14,6 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import inspect
import sys
if not hasattr(sys, "version_info") or sys.version_info < (2, 7, 0, "final", 0):
raise SystemExit("This program requires Python 2.7 or later.")
@ -273,6 +275,46 @@ ArchivePrograms = {
},
}
# List of programs by archive type, which don't support password use
NoPasswordSupportArchivePrograms = {
'bzip2': {
None: ('7z', )
},
'cab': {
None: ('7z', )
},
'zip': {
'create': ('py_zipfile', ),
},
'arj': {
None: ('7z',)
},
'gzip': {
None: ('7z',)
},
'iso': {
None: ('7z',)
},
'cpio': {
None: ('7z', )
},
'rpm': {
None: ('7z', )
},
'deb': {
None: ('7z', )
},
'lzma': {
None: ('7z', )
},
'vhd': {
None: ('7z', )
},
'xz': {
None: ('7z',)
},
}
# List those programs that have different python module names because of
# Python module naming restrictions.
ProgramModules = {
@ -322,7 +364,7 @@ def check_archive_format (format, compression):
raise util.PatoolError("unkonwn archive compression `%s'" % compression)
def find_archive_program (format, command, program=None):
def find_archive_program (format, command, program=None, password=None):
"""Find suitable archive program for given format and mode."""
commands = ArchivePrograms[format]
programs = []
@ -333,6 +375,8 @@ def find_archive_program (format, command, program=None):
for key in (None, command):
if key in commands:
programs.extend(commands[key])
if password is not None:
programs = _remove_command_without_password_support(programs, format, command)
if not programs:
raise util.PatoolError("%s archive format `%s' is not supported" % (command, format))
# return the first existing program
@ -349,6 +393,26 @@ def find_archive_program (format, command, program=None):
raise util.PatoolError("could not find an executable program to %s format %s; candidates are (%s)," % (command, format, ",".join(programs)))
def _remove_command_without_password_support(programs, format, command):
"""Remove programs if they don't support work with password for current
format and command."""
if format not in NoPasswordSupportArchivePrograms:
return programs
no_password_support_commands = NoPasswordSupportArchivePrograms[format]
no_password_support_programs = set()
for key in (None, command):
if key in no_password_support_commands:
for program in no_password_support_commands[key]:
no_password_support_programs.add(program)
programs_with_support = []
for program in programs:
if program not in no_password_support_programs:
programs_with_support.append(program)
if not programs_with_support and programs:
raise util.PatoolError("%s archive format `%s' with password is not supported" % (command, format))
return programs_with_support
def list_formats ():
"""Print information about available archive formats to stdout."""
print("Archive programs of", App)
@ -463,14 +527,14 @@ def cleanup_outdir (outdir, archive):
def _extract_archive(archive, verbosity=0, interactive=True, outdir=None,
program=None, format=None, compression=None):
program=None, format=None, compression=None, password=None):
"""Extract an archive.
@return: output directory if command is 'extract', else None
"""
if format is None:
format, compression = get_archive_format(archive)
check_archive_format(format, compression)
program = find_archive_program(format, 'extract', program=program)
program = find_archive_program(format, 'extract', program=program, password=password)
check_program_compression(archive, 'extract', program, compression)
get_archive_cmdlist = get_archive_cmdlist_func(program, 'extract', format)
if outdir is None:
@ -479,7 +543,7 @@ def _extract_archive(archive, verbosity=0, interactive=True, outdir=None,
else:
do_cleanup_outdir = False
try:
cmdlist = get_archive_cmdlist(archive, compression, program, verbosity, interactive, outdir)
cmdlist = get_archive_cmdlist(archive, compression, program, verbosity, interactive, outdir, password=password)
if cmdlist:
# an empty command list means the get_archive_cmdlist() function
# already handled the command (eg. when it's a builtin Python
@ -502,12 +566,12 @@ def _extract_archive(archive, verbosity=0, interactive=True, outdir=None,
def _create_archive(archive, filenames, verbosity=0, interactive=True,
program=None, format=None, compression=None):
program=None, format=None, compression=None, password=None):
"""Create an archive."""
if format is None:
format, compression = get_archive_format(archive)
check_archive_format(format, compression)
program = find_archive_program(format, 'create', program=program)
program = find_archive_program(format, 'create', program=program, password=password)
check_program_compression(archive, 'create', program, compression)
get_archive_cmdlist = get_archive_cmdlist_func(program, 'create', format)
origarchive = None
@ -516,7 +580,7 @@ def _create_archive(archive, filenames, verbosity=0, interactive=True,
# the arc program mangles the archive name if it contains ".arc"
origarchive = archive
archive = util.tmpfile(dir=os.path.dirname(archive), suffix=".arc")
cmdlist = get_archive_cmdlist(archive, compression, program, verbosity, interactive, filenames)
cmdlist = get_archive_cmdlist(archive, compression, program, verbosity, interactive, filenames, password=password)
if cmdlist:
# an empty command list means the get_archive_cmdlist() function
# already handled the command (eg. when it's a builtin Python
@ -527,18 +591,18 @@ def _create_archive(archive, filenames, verbosity=0, interactive=True,
def _handle_archive(archive, command, verbosity=0, interactive=True,
program=None, format=None, compression=None):
program=None, format=None, compression=None, password=None):
"""Test and list archives."""
if format is None:
format, compression = get_archive_format(archive)
check_archive_format(format, compression)
if command not in ('list', 'test'):
raise util.PatoolError("invalid archive command `%s'" % command)
program = find_archive_program(format, command, program=program)
program = find_archive_program(format, command, program=program, password=password)
check_program_compression(archive, command, program, compression)
get_archive_cmdlist = get_archive_cmdlist_func(program, command, format)
# prepare keyword arguments for command list
cmdlist = get_archive_cmdlist(archive, compression, program, verbosity, interactive)
cmdlist = get_archive_cmdlist(archive, compression, program, verbosity, interactive, password=password)
if cmdlist:
# an empty command list means the get_archive_cmdlist() function
# already handled the command (eg. when it's a builtin Python
@ -558,7 +622,23 @@ def get_archive_cmdlist_func (program, command, format):
raise util.PatoolError(msg)
# get archive handler function (eg. patoolib.programs.star.extract_tar)
try:
return getattr(module, '%s_%s' % (command, format))
archive_cmdlist_func = getattr(module, '%s_%s' % (command, format))
def check_for_password_before_cmdlist_func_call(*args, **kwargs):
""" If password is None, or not set, run command as usual.
If password is set, but can't be accepted raise appropriate
message.
"""
util.log_info("... cmdlist_func = %s %s" % (archive_cmdlist_func, ''))
util.log_info("... kwargs=%s args=%s" % (kwargs, args))
if 'password' in kwargs and kwargs['password'] is None:
kwargs.pop('password')
if 'password' not in kwargs:
return archive_cmdlist_func(*args, **kwargs)
else:
if 'password' in inspect.signature(archive_cmdlist_func).parameters:
return archive_cmdlist_func(*args, **kwargs)
raise util.PatoolError('There is no support for password in %s' % program)
return check_for_password_before_cmdlist_func_call
except AttributeError as msg:
raise util.PatoolError(msg)
@ -593,7 +673,7 @@ def _diff_archives (archive1, archive2, verbosity=0, interactive=True):
shutil.rmtree(tmpdir1, onerror=rmtree_log_error)
def _search_archive(pattern, archive, verbosity=0, interactive=True):
def _search_archive(pattern, archive, verbosity=0, interactive=True, password=None):
"""Search for given pattern in an archive."""
grep = util.find_program("grep")
if not grep:
@ -601,13 +681,13 @@ def _search_archive(pattern, archive, verbosity=0, interactive=True):
raise util.PatoolError(msg)
tmpdir = util.tmpdir()
try:
path = _extract_archive(archive, outdir=tmpdir, verbosity=-1)
path = _extract_archive(archive, outdir=tmpdir, verbosity=-1, password=password)
return util.run_checked([grep, "-r", "-e", pattern, "."], ret_ok=(0, 1), verbosity=1, cwd=path)
finally:
shutil.rmtree(tmpdir, onerror=rmtree_log_error)
def _repack_archive (archive1, archive2, verbosity=0, interactive=True):
def _repack_archive (archive1, archive2, verbosity=0, interactive=True, password=None):
"""Repackage an archive to a different format."""
format1, compression1 = get_archive_format(archive1)
format2, compression2 = get_archive_format(archive2)
@ -617,7 +697,7 @@ def _repack_archive (archive1, archive2, verbosity=0, interactive=True):
return
tmpdir = util.tmpdir()
try:
kwargs = dict(verbosity=verbosity, outdir=tmpdir)
kwargs = dict(verbosity=verbosity, outdir=tmpdir, password=password)
same_format = (format1 == format2 and compression1 and compression2)
if same_format:
# only decompress since the format is the same
@ -628,7 +708,7 @@ def _repack_archive (archive1, archive2, verbosity=0, interactive=True):
olddir = os.getcwd()
os.chdir(path)
try:
kwargs = dict(verbosity=verbosity, interactive=interactive)
kwargs = dict(verbosity=verbosity, interactive=interactive, password=password)
if same_format:
# only compress since the format is the same
kwargs['format'] = compression2
@ -639,7 +719,7 @@ def _repack_archive (archive1, archive2, verbosity=0, interactive=True):
shutil.rmtree(tmpdir, onerror=rmtree_log_error)
def _recompress_archive(archive, verbosity=0, interactive=True):
def _recompress_archive(archive, verbosity=0, interactive=True, password=None):
"""Try to recompress an archive to smaller size."""
format, compression = get_archive_format(archive)
if compression:
@ -651,13 +731,13 @@ def _recompress_archive(archive, verbosity=0, interactive=True):
archive2 = util.get_single_outfile(tmpdir2, base, extension=ext)
try:
# extract
kwargs = dict(verbosity=verbosity, format=format, outdir=tmpdir)
kwargs = dict(verbosity=verbosity, format=format, outdir=tmpdir, password=password)
path = _extract_archive(archive, **kwargs)
# compress to new file
olddir = os.getcwd()
os.chdir(path)
try:
kwargs = dict(verbosity=verbosity, interactive=interactive, format=format)
kwargs = dict(verbosity=verbosity, interactive=interactive, format=format, password=password)
files = tuple(os.listdir(path))
_create_archive(archive2, files, **kwargs)
finally:
@ -679,44 +759,43 @@ def _recompress_archive(archive, verbosity=0, interactive=True):
# the patool library API
def extract_archive(archive, verbosity=0, outdir=None, program=None, interactive=True):
def extract_archive(archive, verbosity=0, outdir=None, program=None, interactive=True, password=None):
"""Extract given archive."""
util.check_existing_filename(archive)
if verbosity >= 0:
util.log_info("Extracting %s ..." % archive)
return _extract_archive(archive, verbosity=verbosity, interactive=interactive, outdir=outdir, program=program)
return _extract_archive(archive, verbosity=verbosity, interactive=interactive, outdir=outdir, program=program, password=password)
def list_archive(archive, verbosity=1, program=None, interactive=True):
def list_archive(archive, verbosity=1, program=None, interactive=True, password=None):
"""List given archive."""
# Set default verbosity to 1 since the listing output should be visible.
util.check_existing_filename(archive)
if verbosity >= 0:
util.log_info("Listing %s ..." % archive)
return _handle_archive(archive, 'list', verbosity=verbosity,
interactive=interactive, program=program)
return _handle_archive(archive, 'list', verbosity=verbosity, interactive=interactive, program=program, password=password)
def test_archive(archive, verbosity=0, program=None, interactive=True):
def test_archive(archive, verbosity=0, program=None, interactive=True, password=None):
"""Test given archive."""
util.check_existing_filename(archive)
if verbosity >= 0:
util.log_info("Testing %s ..." % archive)
res = _handle_archive(archive, 'test', verbosity=verbosity,
interactive=interactive, program=program)
interactive=interactive, program=program, password=password)
if verbosity >= 0:
util.log_info("... tested ok.")
return res
def create_archive(archive, filenames, verbosity=0, program=None, interactive=True):
def create_archive(archive, filenames, verbosity=0, program=None, interactive=True, password=None):
"""Create given archive with given files."""
util.check_new_filename(archive)
util.check_archive_filelist(filenames)
if verbosity >= 0:
util.log_info("Creating %s ..." % archive)
res = _create_archive(archive, filenames, verbosity=verbosity,
interactive=interactive, program=program)
interactive=interactive, program=program, password=password)
if verbosity >= 0:
util.log_info("... %s created." % archive)
return res
@ -733,38 +812,38 @@ def diff_archives(archive1, archive2, verbosity=0, interactive=True):
util.log_info("... no differences found.")
def search_archive(pattern, archive, verbosity=0, interactive=True):
def search_archive(pattern, archive, verbosity=0, interactive=True, password=None):
"""Search pattern in archive members."""
if not pattern:
raise util.PatoolError("empty search pattern")
util.check_existing_filename(archive)
if verbosity >= 0:
util.log_info("Searching %r in %s ..." % (pattern, archive))
res = _search_archive(pattern, archive, verbosity=verbosity, interactive=interactive)
res = _search_archive(pattern, archive, verbosity=verbosity, interactive=interactive, password=password)
if res == 1 and verbosity >= 0:
util.log_info("... %r not found" % pattern)
return res
def repack_archive (archive, archive_new, verbosity=0, interactive=True):
def repack_archive (archive, archive_new, verbosity=0, interactive=True, password=None):
"""Repack archive to different file and/or format."""
util.check_existing_filename(archive)
util.check_new_filename(archive_new)
if verbosity >= 0:
util.log_info("Repacking %s to %s ..." % (archive, archive_new))
res = _repack_archive(archive, archive_new, verbosity=verbosity, interactive=interactive)
res = _repack_archive(archive, archive_new, verbosity=verbosity, interactive=interactive, password=password)
if verbosity >= 0:
util.log_info("... repacking successful.")
return res
def recompress_archive(archive, verbosity=0, interactive=True):
def recompress_archive(archive, verbosity=0, interactive=True, password=None):
"""Recompress an archive to hopefully smaller size."""
util.check_existing_filename(archive)
util.check_writable_filename(archive)
if verbosity >= 0:
util.log_info("Recompressing %s ..." % (archive,))
res = _recompress_archive(archive, verbosity=verbosity, interactive=interactive)
res = _recompress_archive(archive, verbosity=verbosity, interactive=interactive, password=password)
if res and verbosity >= 0:
util.log_info(res)
return 0

View File

@ -15,30 +15,46 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Archive commands for the arc program."""
import os
from ..util import PatoolError
def extract_arc (archive, compression, cmd, verbosity, interactive, outdir):
def _add_password_to_options(options, password):
"""Check password and add it to ARC options."""
if password is None:
return options
if ' ' in password:
raise PatoolError("Password for ARC can't contain spaces.")
options += 'g%s' % password
return options
def extract_arc (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a ARC archive."""
# Since extracted files will be placed in the current directory,
# the cwd argument has to be the output directory.
cmdlist = [cmd, 'x', os.path.abspath(archive)]
options = _add_password_to_options('x', password)
cmdlist = [cmd, options, os.path.abspath(archive)]
return (cmdlist, {'cwd': outdir})
def list_arc (archive, compression, cmd, verbosity, interactive):
def list_arc (archive, compression, cmd, verbosity, interactive, password=None):
"""List a ARC archive."""
cmdlist = [cmd]
if verbosity > 1:
cmdlist.append('v')
cmdlist.append(_add_password_to_options('v', password))
else:
cmdlist.append('l')
cmdlist.append(_add_password_to_options('l', password))
cmdlist.append(archive)
return cmdlist
def test_arc (archive, compression, cmd, verbosity, interactive):
"""Test a ARC archive."""
return [cmd, 't', archive]
def create_arc (archive, compression, cmd, verbosity, interactive, filenames):
def test_arc (archive, compression, cmd, verbosity, interactive, password=None):
"""Test a ARC archive."""
return [cmd, _add_password_to_options('t', password), archive]
def create_arc (archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create a ARC archive."""
cmdlist = [cmd, 'a', archive]
cmdlist = [cmd, _add_password_to_options('a', password), archive]
cmdlist.extend(filenames)
return cmdlist

View File

@ -14,19 +14,33 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Archive commands for the arj program."""
from ..util import PatoolError
def extract_arj (archive, compression, cmd, verbosity, interactive, outdir):
def _get_password_switch(password):
"""Check password and return password switch for ARJ."""
if ' ' in password:
raise PatoolError("Password for ARJ can't contain spaces.")
return '-g%s' % password
def _maybe_add_password(cmdlist, password):
if password:
cmdlist.append(_get_password_switch(password))
def extract_arj (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract an ARJ archive."""
cmdlist = [cmd, 'x', '-r']
_maybe_add_password(cmdlist, password)
if not interactive:
cmdlist.append('-y')
cmdlist.extend([archive, outdir])
return cmdlist
def list_arj (archive, compression, cmd, verbosity, interactive):
def list_arj (archive, compression, cmd, verbosity, interactive, password=None):
"""List an ARJ archive."""
cmdlist = [cmd]
_maybe_add_password(cmdlist, password)
if verbosity > 1:
cmdlist.append('v')
else:
@ -37,18 +51,20 @@ def list_arj (archive, compression, cmd, verbosity, interactive):
return cmdlist
def test_arj (archive, compression, cmd, verbosity, interactive):
def test_arj (archive, compression, cmd, verbosity, interactive, password=None):
"""Test an ARJ archive."""
cmdlist = [cmd, 't', '-r']
_maybe_add_password(cmdlist, password)
if not interactive:
cmdlist.append('-y')
cmdlist.append(archive)
return cmdlist
def create_arj (archive, compression, cmd, verbosity, interactive, filenames):
def create_arj (archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create an ARJ archive."""
cmdlist = [cmd, 'a', '-r']
_maybe_add_password(cmdlist, password)
if not interactive:
cmdlist.append('-y')
cmdlist.append(archive)

View File

@ -15,21 +15,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Archive commands for the 7z program."""
def extract_7z(archive, compression, cmd, verbosity, interactive, outdir):
def _maybe_add_password(cmdlist, password):
if password:
cmdlist.append('-p%s' % password)
def extract_7z(archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a 7z archive."""
cmdlist = [cmd, 'x']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-o%s' % outdir, '--', archive])
return cmdlist
def extract_7z_singlefile(archive, compression, cmd, verbosity, interactive, outdir):
def extract_7z_singlefile(archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a singlefile archive (eg. gzip or bzip2) with '7z e'.
This makes sure a single file and no subdirectories are created,
which would cause errors with patool repack."""
cmdlist = [cmd, 'e']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-o%s' % outdir, '--', archive])
return cmdlist
@ -51,11 +57,12 @@ extract_zip = \
extract_vhd = \
extract_7z
def list_7z (archive, compression, cmd, verbosity, interactive):
def list_7z (archive, compression, cmd, verbosity, interactive, password=None):
"""List a 7z archive."""
cmdlist = [cmd, 'l']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['--', archive])
return cmdlist
@ -76,11 +83,12 @@ list_bzip2 = \
list_7z
def test_7z (archive, compression, cmd, verbosity, interactive):
def test_7z (archive, compression, cmd, verbosity, interactive, password=None):
"""Test a 7z archive."""
cmdlist = [cmd, 't']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['--', archive])
return cmdlist
@ -101,51 +109,56 @@ test_bzip2 = \
test_7z
def create_7z(archive, compression, cmd, verbosity, interactive, filenames):
def create_7z(archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create a 7z archive."""
cmdlist = [cmd, 'a']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-t7z', '-mx=9', '--', archive])
cmdlist.extend(filenames)
return cmdlist
def create_zip(archive, compression, cmd, verbosity, interactive, filenames):
def create_zip(archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create a ZIP archive."""
cmdlist = [cmd, 'a']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-tzip', '-mx=9', '--', archive])
cmdlist.extend(filenames)
return cmdlist
def create_xz(archive, compression, cmd, verbosity, interactive, filenames):
def create_xz(archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create an XZ archive."""
cmdlist = [cmd, 'a']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-txz', '-mx=9', '--', archive])
cmdlist.extend(filenames)
return cmdlist
def create_gzip(archive, compression, cmd, verbosity, interactive, filenames):
def create_gzip(archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create a GZIP archive."""
cmdlist = [cmd, 'a']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-tgzip', '-mx=9', '--', archive])
cmdlist.extend(filenames)
return cmdlist
def create_bzip2(archive, compression, cmd, verbosity, interactive, filenames):
def create_bzip2(archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create a BZIP2 archive."""
cmdlist = [cmd, 'a']
if not interactive:
cmdlist.append('-y')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['-tbzip2', '-mx=9', '--', archive])
cmdlist.extend(filenames)
return cmdlist

View File

@ -22,10 +22,12 @@ import os
READ_SIZE_BYTES = 1024*1024
def list_zip(archive, compression, cmd, verbosity, interactive):
def list_zip(archive, compression, cmd, verbosity, interactive, password=None):
"""List member of a ZIP archive with the zipfile Python module."""
try:
with zipfile.ZipFile(archive, "r") as zfile:
if password:
zfile.setpassword(pwd=password.encode())
for name in zfile.namelist():
if verbosity >= 0:
print(name)
@ -36,11 +38,13 @@ def list_zip(archive, compression, cmd, verbosity, interactive):
test_zip = list_zip
def extract_zip(archive, compression, cmd, verbosity, interactive, outdir):
def extract_zip(archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a ZIP archive with the zipfile Python module."""
try:
if password:
password = password.encode()
with zipfile.ZipFile(archive) as zfile:
zfile.extractall(outdir)
zfile.extractall(outdir, pwd=password)
except Exception as err:
msg = "error extracting %s: %s" % (archive, err)
raise util.PatoolError(msg)

View File

@ -16,15 +16,17 @@
"""Archive commands for the rar program."""
import os
def extract_rar (archive, compression, cmd, verbosity, interactive, outdir):
def extract_rar (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a RAR archive."""
cmdlist = [cmd, 'x']
if not interactive:
cmdlist.extend(['-p-', '-y'])
if password:
cmdlist.append('-p%s' % password)
cmdlist.extend(['--', os.path.abspath(archive)])
return (cmdlist, {'cwd': outdir})
def list_rar (archive, compression, cmd, verbosity, interactive):
def list_rar (archive, compression, cmd, verbosity, interactive, password=None):
"""List a RAR archive."""
cmdlist = [cmd]
if verbosity > 1:
@ -33,22 +35,28 @@ def list_rar (archive, compression, cmd, verbosity, interactive):
cmdlist.append('l')
if not interactive:
cmdlist.extend(['-p-', '-y'])
if password:
cmdlist.append('-p%s' % password)
cmdlist.extend(['--', archive])
return cmdlist
def test_rar (archive, compression, cmd, verbosity, interactive):
def test_rar (archive, compression, cmd, verbosity, interactive, password=None):
"""Test a RAR archive."""
cmdlist = [cmd, 't']
if not interactive:
cmdlist.extend(['-p-', '-y'])
if password:
cmdlist.append('-p%s' % password)
cmdlist.extend(['--', archive])
return cmdlist
def create_rar (archive, compression, cmd, verbosity, interactive, filenames):
def create_rar (archive, compression, cmd, verbosity, interactive, filenames, password=None):
"""Create a RAR archive."""
cmdlist = [cmd, 'a']
if not interactive:
cmdlist.append('-y')
if password:
cmdlist.append('-p%s' % password)
cmdlist.extend(['-r', '-m5', '--', archive])
cmdlist.extend(filenames)
return cmdlist

View File

@ -15,24 +15,32 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Archive commands for the unace program."""
def extract_ace (archive, compression, cmd, verbosity, interactive, outdir):
def extract_ace (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract an ACE archive."""
cmdlist = [cmd, 'x']
if not outdir.endswith('/'):
outdir += '/'
if password:
cmdlist.append('-p%s' % password)
cmdlist.extend([archive, outdir])
return cmdlist
def list_ace (archive, compression, cmd, verbosity, interactive):
def list_ace (archive, compression, cmd, verbosity, interactive, password=None):
"""List an ACE archive."""
cmdlist = [cmd]
if verbosity > 1:
cmdlist.append('v')
else:
cmdlist.append('l')
if password:
cmdlist.append('-p%s' % password)
cmdlist.append(archive)
return cmdlist
def test_ace (archive, compression, cmd, verbosity, interactive):
def test_ace (archive, compression, cmd, verbosity, interactive, password=None):
"""Test an ACE archive."""
return [cmd, 't', archive]
cmdlist = [cmd, 't']
if password:
cmdlist.append('-p%s' % password)
cmdlist.append(archive)
return cmdlist

View File

@ -15,13 +15,24 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Archive commands for the unalz program."""
def extract_alzip (archive, compression, cmd, verbosity, interactive, outdir):
def _maybe_add_password(cmdlist, password):
if password:
cmdlist.extend(['-pwd', password])
def extract_alzip (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a ALZIP archive."""
return [cmd, '-d', outdir, archive]
cmdlist = [cmd, '-d', outdir]
_maybe_add_password(cmdlist, password)
cmdlist.append(archive)
return cmdlist
def list_alzip (archive, compression, cmd, verbosity, interactive):
def list_alzip (archive, compression, cmd, verbosity, interactive, password=None):
"""List a ALZIP archive."""
return [cmd, '-l', archive]
cmdlist = [cmd, '-l']
_maybe_add_password(cmdlist, password)
cmdlist.append(archive)
return cmdlist
test_alzip = list_alzip

View File

@ -15,26 +15,33 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Archive commands for the unzip program."""
def extract_zip (archive, compression, cmd, verbosity, interactive, outdir):
def _maybe_add_password(cmdlist, password):
if password:
cmdlist.extend(['-P', password])
def extract_zip (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a ZIP archive."""
cmdlist = [cmd]
if verbosity > 1:
cmdlist.append('-v')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['--', archive, '-d', outdir])
return cmdlist
def list_zip (archive, compression, cmd, verbosity, interactive):
def list_zip (archive, compression, cmd, verbosity, interactive, password=None):
"""List a ZIP archive."""
cmdlist = [cmd, '-l']
if verbosity > 1:
cmdlist.append('-v')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['--', archive])
return cmdlist
def test_zip (archive, compression, cmd, verbosity, interactive):
def test_zip (archive, compression, cmd, verbosity, interactive, password=None):
"""Test a ZIP archive."""
cmdlist = [cmd, '-t']
if verbosity > 1:
cmdlist.append('-v')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['--', archive])
return cmdlist

View File

@ -17,26 +17,38 @@
from .. import util
def extract_dms (archive, compression, cmd, verbosity, interactive, outdir):
def _maybe_add_password(cmdlist, password):
if password:
cmdlist.extend(['-p', password])
def extract_dms (archive, compression, cmd, verbosity, interactive, outdir, password=None):
"""Extract a DMS archive."""
check_archive_ext(archive)
cmdlist = [cmd, '-d', outdir]
if verbosity > 1:
cmdlist.append('-v')
_maybe_add_password(cmdlist, password)
cmdlist.extend(['u', archive])
return cmdlist
def list_dms (archive, compression, cmd, verbosity, interactive):
def list_dms (archive, compression, cmd, verbosity, interactive, password=None):
"""List a DMS archive."""
check_archive_ext(archive)
return [cmd, 'v', archive]
cmdlist = [cmd, 'v']
_maybe_add_password(cmdlist, password)
cmdlist.append(archive)
return cmdlist
def test_dms (archive, compression, cmd, verbosity, interactive):
def test_dms (archive, compression, cmd, verbosity, interactive, password=None):
"""Test a DMS archive."""
check_archive_ext(archive)
return [cmd, 't', archive]
cmdlist = [cmd, 't']
_maybe_add_password(cmdlist, password)
cmdlist.append(archive)
return cmdlist
def check_archive_ext (archive):

View File

@ -49,6 +49,10 @@ class ArchiveTest (unittest.TestCase):
# set program to use for archive handling in subclass
program = None
# set password for test with password
password = None
# default archive basename to check
filename = 't'
def archive_commands (self, filename, **kwargs):
"""Run archive commands list, test, extract and create.
@ -75,7 +79,7 @@ class ArchiveTest (unittest.TestCase):
try:
olddir = patoolib.util.chdir(tmpdir)
try:
output = patoolib.extract_archive(archive, program=self.program, verbosity=verbosity, interactive=False)
output = patoolib.extract_archive(archive, program=self.program, verbosity=verbosity, interactive=False, password=self.password)
if check:
self.check_extracted_archive(archive, output, check)
finally:
@ -117,13 +121,13 @@ class ArchiveTest (unittest.TestCase):
"""Test archive listing."""
archive = os.path.join(datadir, filename)
for verbosity in (-1, 0, 1, 2):
patoolib.list_archive(archive, program=self.program, verbosity=verbosity, interactive=False)
patoolib.list_archive(archive, program=self.program, verbosity=verbosity, interactive=False, password=self.password)
def archive_test (self, filename):
"""Test archive testing."""
archive = os.path.join(datadir, filename)
for verbosity in (-1, 0, 1, 2):
patoolib.test_archive(archive, program=self.program, verbosity=verbosity, interactive=False)
patoolib.test_archive(archive, program=self.program, verbosity=verbosity, interactive=False, password=self.password)
def archive_create (self, archive, srcfiles=None, check=Content.Recursive):
"""Test archive creation."""
@ -156,7 +160,7 @@ class ArchiveTest (unittest.TestCase):
try:
archive = os.path.join(tmpdir, archive)
self.assertTrue(os.path.isabs(archive), "archive path is not absolute: %r" % archive)
patoolib.create_archive(archive, srcfiles, verbosity=verbosity, interactive=False, program=program)
patoolib.create_archive(archive, srcfiles, verbosity=verbosity, interactive=False, program=program, password=self.password)
self.assertTrue(os.path.isfile(archive))
self.check_created_archive_with_test(archive)
self.check_created_archive_with_diff(archive, srcfiles)
@ -184,7 +188,7 @@ class ArchiveTest (unittest.TestCase):
program = '7z'
elif self.program == 'shar':
return
command(archive, program=program)
command(archive, program=program, password=self.password)
def check_created_archive_with_diff(self, archive, srcfiles):
"""Extract created archive again and compare the contents."""
@ -208,7 +212,7 @@ class ArchiveTest (unittest.TestCase):
try:
olddir = patoolib.util.chdir(tmpdir)
try:
output = patoolib.extract_archive(archive, program=program, interactive=False)
output = patoolib.extract_archive(archive, program=program, interactive=False, password=self.password)
if len(srcfiles) == 1:
source = os.path.join(datadir, srcfiles[0])
patoolib.util.run_checked([diff, "-urN", source, output])

View File

@ -116,6 +116,47 @@ class Test7z (ArchiveTest):
@needs_codec(program, 'rar')
def test_7z_rar_file (self):
# only succeeds with the rar module for 7z installed
self.archive_list('t.rar.foo')
self.archive_extract('t.rar.foo')
self.archive_test('t.rar.foo')
self.archive_list(self.filename + '.rar.foo')
self.archive_extract(self.filename + '.rar.foo')
self.archive_test(self.filename + '.rar.foo')
class Test7zPassword(ArchiveTest):
program = '7z'
password = 'thereisnotry'
@needs_program(program)
def test_7z (self):
self.archive_commands('p .7z')
self.archive_commands('p.zip')
self.archive_commands('p.cbz')
self.archive_list('p.arj')
self.archive_extract('p.arj')
self.archive_test('p.arj')
@needs_codec(program, 'rar')
def test_7z_rar (self):
# only succeeds with the rar module for 7z installed
self.archive_list('p.rar')
self.archive_extract('p.rar')
self.archive_test('p.rar')
@needs_program('file')
@needs_program(program)
def test_7z_file (self):
self.archive_commands('p.7z.foo', skip_create=True)
self.archive_commands('p.zip.foo', skip_create=True)
self.archive_commands('p.cbz.foo', skip_create=True)
self.archive_list('p.arj.foo')
self.archive_extract('p.arj.foo')
self.archive_test('p.arj.foo')
@needs_program('file')
@needs_codec(program, 'rar')
def test_7z_rar_file (self):
# only succeeds with the rar module for 7z installed
self.archive_list('p.rar.foo')
self.archive_extract('p.rar.foo')
self.archive_test('p.rar.foo')

View File

@ -22,10 +22,15 @@ class TestArc(ArchiveTest):
@needs_program(program)
def test_arc(self):
self.archive_commands('t.arc', check=Content.Multifile)
self.archive_commands(self.filename + '.arc', check=Content.Multifile)
@needs_program('file')
@needs_program(program)
def test_arc_file(self):
self.archive_commands('t.arc.foo', check=Content.Multifile, skip_create=True)
self.archive_commands(self.filename + '.arc.foo', check=Content.Multifile, skip_create=True)
class TestArcPassword(TestArc):
password = 'thereisnotry'
filename = 'p'

View File

@ -22,10 +22,15 @@ class TestArj (ArchiveTest):
@needs_program(program)
def test_arj(self):
self.archive_commands('t.arj')
self.archive_commands(self.filename + '.arj')
@needs_program('file')
@needs_program(program)
def test_arj_file(self):
self.archive_commands('t.arj.foo', skip_create=True)
self.archive_commands(self.filename + '.arj.foo', skip_create=True)
class TestArjPassword(TestArj):
password = 'thereisnotry'
filename = 'p'

View File

@ -21,11 +21,15 @@ class TestPyzipfile (ArchiveTest):
program = 'py_zipfile'
def test_py_zipfile(self):
self.archive_commands('t.zip')
self.archive_commands('t.cbz')
self.archive_commands(self.filename + '.zip')
self.archive_commands(self.filename + '.cbz')
@needs_program('file')
def test_py_zipfile_file(self):
self.archive_commands('t.zip.foo', skip_create=True)
self.archive_commands('t.cbz.foo', skip_create=True)
self.archive_commands(self.filename + '.zip.foo', skip_create=True)
self.archive_commands(self.filename + '.cbz.foo', skip_create=True)
class TestPyzipPasswordfile (TestPyzipfile):
filename = 'p'
password = 'thereisnotry'

View File

@ -22,12 +22,17 @@ class TestRar (ArchiveTest):
@needs_program(program)
def test_rar(self):
self.archive_commands('t.rar')
self.archive_commands('t.cbr')
self.archive_commands(self.filename + '.rar')
self.archive_commands(self.filename + '.cbr')
@needs_program('file')
@needs_program(program)
def test_rar_file(self):
self.archive_commands('t.rar.foo', skip_create=True)
self.archive_commands('t.cbr.foo', skip_create=True)
self.archive_commands(self.filename + '.rar.foo', skip_create=True)
self.archive_commands(self.filename + '.cbr.foo', skip_create=True)
class TestRarPassword (TestRar):
filename = 'p'
password = 'thereisnotry'

View File

@ -22,19 +22,26 @@ class TestUnace (ArchiveTest):
@needs_program(program)
def test_unace(self):
self.archive_list('t.ace')
self.archive_test('t.ace')
self.archive_extract('t.ace')
self.archive_list('t.cba')
self.archive_test('t.cba')
self.archive_extract('t.cba')
self.archive_list(self.filename + '.ace')
self.archive_test(self.filename + '.ace')
self.archive_extract(self.filename + '.ace')
self.archive_list(self.filename + '.cba')
self.archive_test(self.filename + '.cba')
self.archive_extract(self.filename + '.cba')
@needs_program('file')
@needs_program(program)
def test_unace_file(self):
self.archive_list('t.ace.foo')
self.archive_test('t.ace.foo')
self.archive_extract('t.ace.foo')
self.archive_list('t.cba.foo')
self.archive_test('t.cba.foo')
self.archive_extract('t.cba.foo')
self.archive_list(self.filename + '.ace.foo')
self.archive_test(self.filename + '.ace.foo')
self.archive_extract(self.filename + '.ace.foo')
self.archive_list(self.filename + '.cba.foo')
self.archive_test(self.filename + '.cba.foo')
self.archive_extract(self.filename + '.cba.foo')
# TODO: add p.ace, p.ace.foo, p.cba, p.cba.foo with password to repository
# class TestUnacePassword (TestUnace):
#
# filename = 'p'
# password = 'thereisnotry'

View File

@ -56,3 +56,28 @@ class TestUnzip (ArchiveTest):
self.archive_extract('t.apk.foo', check=None)
self.archive_list('t.apk.foo')
self.archive_test('t.apk.foo')
class TestUnzipPassword (ArchiveTest):
program = 'unzip'
password = 'thereisnotry'
@needs_program(program)
def test_unzip (self):
self.archive_extract('p.zip', check=None)
self.archive_list('p.zip')
self.archive_test('p.zip')
self.archive_extract('p.cbz', check=None)
self.archive_list('p.cbz')
self.archive_test('p.cbz')
@needs_program('file')
@needs_program(program)
def test_unzip_file (self):
self.archive_extract('p.zip.foo', check=None)
self.archive_list('p.zip.foo')
self.archive_test('p.zip.foo')
self.archive_extract('p.cbz.foo', check=None)
self.archive_list('p.cbz.foo')
self.archive_test('p.cbz.foo')

BIN
tests/data/p .7z Normal file

Binary file not shown.

BIN
tests/data/p.7z.foo Normal file

Binary file not shown.

BIN
tests/data/p.arc Normal file

Binary file not shown.

BIN
tests/data/p.arc.foo Normal file

Binary file not shown.

BIN
tests/data/p.arj Normal file

Binary file not shown.

BIN
tests/data/p.arj.foo Normal file

Binary file not shown.

BIN
tests/data/p.cbz Normal file

Binary file not shown.

BIN
tests/data/p.cbz.foo Normal file

Binary file not shown.

BIN
tests/data/p.rar Normal file

Binary file not shown.

BIN
tests/data/p.rar.foo Normal file

Binary file not shown.

BIN
tests/data/p.zip Normal file

Binary file not shown.

BIN
tests/data/p.zip.foo Normal file

Binary file not shown.