Skip to content

Commit

Permalink
Add password options for 7z, zip, rar and arj (#51)
Browse files Browse the repository at this point in the history
* Add password option for 7z, zip and rar

* Remove f-strings for v2 compatibility

* Add password support for unar and arj

* Add tests

* Remove f-strings (again)

* Format format-strings
  • Loading branch information
sr-verde authored Jan 16, 2024
1 parent 37b7753 commit bda685a
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 12 deletions.
78 changes: 66 additions & 12 deletions dtrx/dtrx.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ def __init__(self, filename, encoding):
self.filename = os.path.realpath(filename)
self.encoding = encoding
self.ignore_pw = False
self.password = None
self.file_count = 0
self.included_archives = []
self.target = None
Expand Down Expand Up @@ -279,10 +280,9 @@ def wait_for_exit(self, pipe):
try:
return pipe.wait(timeout=1)
except subprocess.TimeoutExpired:
logging.debug("timeout hit..")
logging.debug("timeout hit...")
self.timeout_check(pipe)
# Verify that we're not trying to extract password-protected
# archives in non-interactive mode
# Verify that we're not waiting for a password in non-interactive mode
if self.pw_prompted and self.ignore_pw:
pipe.kill()
# Whatever extractor we're using probably left the
Expand All @@ -292,7 +292,7 @@ def wait_for_exit(self, pipe):
self.stderr = ""
raise ExtractorError(
"cannot extract encrypted archive '%s' in non-interactive mode"
% (self.filename)
" without a password" % (self.filename)
)

def send_stdout_to_dev_null(self):
Expand Down Expand Up @@ -417,8 +417,9 @@ def extract_archive(self):
self.pipe(self.extract_pipe)
self.run_pipes()

def extract(self, ignore_passwd=False):
def extract(self, ignore_passwd=False, password=None):
self.ignore_pw = ignore_passwd
self.password = password
try:
self.target = tempfile.mkdtemp(prefix=".dtrx-", dir=".")
except (OSError, IOError) as error:
Expand Down Expand Up @@ -484,8 +485,9 @@ def get_filenames(self):
raise ExtractorError("doesn't look like a compressed file")
yield self.basename()

def extract(self, ignore_passwd=False):
def extract(self, ignore_passwd=False, password=None):
self.ignore_pw = ignore_passwd
self.password = password
self.content_type = ONE_ENTRY_KNOWN
self.content_name = self.basename()
self.contents = None
Expand Down Expand Up @@ -651,9 +653,18 @@ def get_filenames(self):

class ZipExtractor(NoPipeExtractor):
file_type = "Zip file"
extract_command = ["unzip", "-q"]
list_command = ["zipinfo", "-1"]

@property
def extract_command(self):
"""
Returns the extraction command and adds a password if given.
"""
cmd = ["unzip", "-q"]
if self.password:
cmd.append("-P %s" % (self.password,))
return cmd

def is_fatal_error(self, status):
return (status or 0) > 1

Expand Down Expand Up @@ -702,10 +713,19 @@ def get_filenames(self):

class SevenExtractor(NoPipeExtractor):
file_type = "7z file"
extract_command = ["7z", "x"]
list_command = ["7z", "l"]
border_re = re.compile("^[- ]+$")

@property
def extract_command(self):
"""
Returns the extraction command and adds a password if given.
"""
cmd = ["7z", "x"]
if self.password:
cmd.append("-p%s" % (self.password,))
return cmd

def get_filenames(self):
fn_index = None
for line in NoPipeExtractor.get_filenames(self):
Expand Down Expand Up @@ -809,10 +829,19 @@ def basename(self):

class RarExtractor(NoPipeExtractor):
file_type = "RAR archive"
extract_command = ["unrar", "x"]
list_command = ["unrar", "v"]
border_re = re.compile("^-+$")

@property
def extract_command(self):
"""
Returns the extraction command and adds a password if given.
"""
cmd = ["unrar", "x"]
if self.password:
cmd.append("-p%s" % (self.password,))
return cmd

def get_filenames(self):
inside = False
isfile = True
Expand Down Expand Up @@ -843,9 +872,18 @@ def timeout_check(self, pipe):

class UnarchiverExtractor(NoPipeExtractor):
file_type = "RAR archive"
extract_command = ["unar", "-D"]
list_command = ["lsar"]

@property
def extract_command(self):
"""
Returns the extraction command and adds a password if given.
"""
cmd = ["unar", "-D"]
if self.password:
cmd.append("-p %s" % (self.password,))
return cmd

def get_filenames(self):
output = NoPipeExtractor.get_filenames(self)
next(output)
Expand All @@ -856,10 +894,19 @@ def get_filenames(self):

class ArjExtractor(NoPipeExtractor):
file_type = "ARJ archive"
extract_command = ["arj", "x", "-y"]
list_command = ["arj", "v"]
prefix_re = re.compile(r"^\d+\)\s+")

@property
def extract_command(self):
"""
Returns the extraction command and adds a password if given.
"""
cmd = ["arj", "x", "-y"]
if self.password:
cmd.append("-g%s" % (self.password,))
return cmd

def get_filenames(self):
for line in NoPipeExtractor.get_filenames(self):
match = self.prefix_re.match(line)
Expand Down Expand Up @@ -1482,7 +1529,7 @@ def reverser(x, y):
def run(self, filename, extractor):
self.current_filename = filename
error = (
self.report(extractor.extract, self.options.batch)
self.report(extractor.extract, self.options.batch, self.options.password)
or self.report(self.get_handler, extractor)
or self.report(self.current_handler.handle)
or self.report(self.show_extraction, extractor)
Expand Down Expand Up @@ -1632,6 +1679,13 @@ def parse_options(self, arguments):
default=False,
help="don't ask how to handle special cases",
)
parser.add_option(
"-p",
"--password",
dest="password",
default=None,
help="provide a password for password-protected archives",
)
parser.add_option(
"-o",
"--overwrite",
Expand Down
Binary file added tests/test-pw.7z
Binary file not shown.
Binary file added tests/test-pw.arj
Binary file not shown.
Binary file added tests/test-pw.rar
Binary file not shown.
44 changes: 44 additions & 0 deletions tests/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,50 @@
grep: "cannot extract encrypted archive"
cleanup: stty -F /dev/stdout echo

- name: password zip noninteractive with password
filenames: test-pw.zip
options: "-n -p yolo"
baseline: |
mkdir test-pw
cd test-pw
unzip -P yolo -q $1
posttest: |
exec [ "$(cat test-pw)" = "test-pw" ]
cleanup: rm -rf test-pw

- name: password 7z noninteractive with password
filenames: test-pw.7z
options: "-n -p yolo"
baseline: |
mkdir test-pw
cd test-pw
7z x $1 -pyolo -q
posttest: |
exec [ "$(cat test-pw)" = "test-pw" ]
cleanup: rm -rf test-pw

- name: password rar noninteractive with password
filenames: test-pw.rar
options: "-n -p yolo"
baseline: |
mkdir test-pw
cd test-pw
unrar e -pyolo -q $1
posttest: |
exec [ "$(cat test-pw)" = "test-pw" ]
cleanup: rm -rf test-pw

- name: password arj noninteractive with password
filenames: test-pw.7z
options: "-n -p yolo"
baseline: |
mkdir test-pw
cd test-pw
arj x -gyolo $1
posttest: |
exec [ "$(cat test-pw)" = "test-pw" ]
cleanup: rm -rf test-pw

- name: brotli
filenames: test-text.br
baseline: |
Expand Down

0 comments on commit bda685a

Please sign in to comment.