diff --git a/src/calibre/utils/localunzip.py b/src/calibre/utils/localunzip.py index 8b2b2f58b4..8af2cd736b 100644 --- a/src/calibre/utils/localunzip.py +++ b/src/calibre/utils/localunzip.py @@ -33,6 +33,20 @@ LocalHeader = namedtuple('LocalHeader', 'filename extra') +if hasattr(sys, 'getwindowsversion'): + windows_reserved_filenames = ( + 'CON', 'PRN', 'AUX', 'CLOCK$', 'NUL' 'COM0', 'COM1', 'COM2', 'COM3', + 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9' 'LPT0', 'LPT1', 'LPT2', + 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9') + + def is_reserved_filename(x): + base = x.partition('.')[0].upper() + return base in windows_reserved_filenames +else: + def is_reserved_filename(x): + return False + + def decode_arcname(name): if isinstance(name, bytes): from calibre.ebooks.chardet import detect @@ -207,11 +221,18 @@ def _extractall(f, path=None, file_info=None): if not os.path.exists(bdir): os.makedirs(bdir) dest = os.path.join(path, *parts) - with open(dest, 'wb') as o: + try: + df = open(dest, 'wb') + except EnvironmentError: + if is_reserved_filename(os.path.basename(dest)): + raise ValueError('This ZIP file contains a file with a reserved filename' + ' that cannot be processed on Windows: {}'.format(os.path.basename(dest))) + raise + with df: if header.compression_method == ZIP_STORED: - copy_stored_file(f, header.compressed_size, o) + copy_stored_file(f, header.compressed_size, df) else: - copy_compressed_file(f, header.compressed_size, o) + copy_compressed_file(f, header.compressed_size, df) else: f.seek(f.tell()+seekval)