2015-01-21 21:15:23 -05:00
|
|
|
# Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov
|
|
|
|
#
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
# a copy of this software and associated documentation files (the
|
|
|
|
# "Software"), to deal in the Software without restriction, including
|
|
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
# the following conditions:
|
|
|
|
#
|
|
|
|
# The above copyright notice and this permission notice shall be
|
|
|
|
# included in all copies or substantial portions of the Software.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
|
|
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
|
|
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
# SOFTWARE.
|
|
|
|
|
|
|
|
"""
|
|
|
|
pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll.
|
|
|
|
|
|
|
|
It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple,
|
|
|
|
stable and foolproof.
|
|
|
|
Notice that it has INCOMPATIBLE interface.
|
|
|
|
|
|
|
|
It enables reading and unpacking of archives created with the
|
|
|
|
RAR/WinRAR archivers. There is a low-level interface which is very
|
|
|
|
similar to the C interface provided by UnRAR. There is also a
|
|
|
|
higher level interface which makes some common operations easier.
|
|
|
|
"""
|
|
|
|
|
|
|
|
__version__ = '0.99.6'
|
|
|
|
|
|
|
|
try:
|
|
|
|
WindowsError
|
|
|
|
in_windows = True
|
|
|
|
except NameError:
|
|
|
|
in_windows = False
|
|
|
|
|
|
|
|
if in_windows:
|
|
|
|
from windows import RarFileImplementation
|
|
|
|
else:
|
|
|
|
from unix import RarFileImplementation
|
|
|
|
|
|
|
|
|
|
|
|
import fnmatch, time, weakref
|
|
|
|
|
|
|
|
class RarInfo(object):
|
|
|
|
"""Represents a file header in an archive. Don't instantiate directly.
|
|
|
|
Use only to obtain information about file.
|
|
|
|
YOU CANNOT EXTRACT FILE CONTENTS USING THIS OBJECT.
|
|
|
|
USE METHODS OF RarFile CLASS INSTEAD.
|
|
|
|
|
|
|
|
Properties:
|
|
|
|
index - index of file within the archive
|
|
|
|
filename - name of the file in the archive including path (if any)
|
|
|
|
datetime - file date/time as a struct_time suitable for time.strftime
|
|
|
|
isdir - True if the file is a directory
|
|
|
|
size - size in bytes of the uncompressed file
|
|
|
|
comment - comment associated with the file
|
|
|
|
|
|
|
|
Note - this is not currently intended to be a Python file-like object.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, rarfile, data):
|
|
|
|
self.rarfile = weakref.proxy(rarfile)
|
|
|
|
self.index = data['index']
|
|
|
|
self.filename = data['filename']
|
|
|
|
self.isdir = data['isdir']
|
|
|
|
self.size = data['size']
|
|
|
|
self.datetime = data['datetime']
|
|
|
|
self.comment = data['comment']
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
try :
|
|
|
|
arcName = self.rarfile.archiveName
|
|
|
|
except ReferenceError:
|
|
|
|
arcName = "[ARCHIVE_NO_LONGER_LOADED]"
|
|
|
|
return '<RarInfo "%s" in "%s">' % (self.filename, arcName)
|
|
|
|
|
|
|
|
class RarFile(RarFileImplementation):
|
|
|
|
|
|
|
|
def __init__(self, archiveName, password=None):
|
|
|
|
"""Instantiate the archive.
|
|
|
|
|
|
|
|
archiveName is the name of the RAR file.
|
|
|
|
password is used to decrypt the files in the archive.
|
|
|
|
|
|
|
|
Properties:
|
|
|
|
comment - comment associated with the archive
|
|
|
|
|
|
|
|
>>> print RarFile('test.rar').comment
|
|
|
|
This is a test.
|
|
|
|
"""
|
|
|
|
self.archiveName = archiveName
|
|
|
|
RarFileImplementation.init(self, password)
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
self.destruct()
|
|
|
|
|
|
|
|
def infoiter(self):
|
|
|
|
"""Iterate over all the files in the archive, generating RarInfos.
|
|
|
|
|
|
|
|
>>> import os
|
|
|
|
>>> for fileInArchive in RarFile('test.rar').infoiter():
|
|
|
|
... print os.path.split(fileInArchive.filename)[-1],
|
|
|
|
... print fileInArchive.isdir,
|
|
|
|
... print fileInArchive.size,
|
|
|
|
... print fileInArchive.comment,
|
|
|
|
... print tuple(fileInArchive.datetime)[0:5],
|
|
|
|
... print time.strftime('%a, %d %b %Y %H:%M', fileInArchive.datetime)
|
|
|
|
test True 0 None (2003, 6, 30, 1, 59) Mon, 30 Jun 2003 01:59
|
|
|
|
test.txt False 20 None (2003, 6, 30, 2, 1) Mon, 30 Jun 2003 02:01
|
|
|
|
this.py False 1030 None (2002, 2, 8, 16, 47) Fri, 08 Feb 2002 16:47
|
|
|
|
"""
|
|
|
|
for params in RarFileImplementation.infoiter(self):
|
|
|
|
yield RarInfo(self, params)
|
|
|
|
|
|
|
|
def infolist(self):
|
|
|
|
"""Return a list of RarInfos, descripting the contents of the archive."""
|
|
|
|
return list(self.infoiter())
|
|
|
|
|
|
|
|
def read_files(self, condition='*'):
|
|
|
|
"""Read specific files from archive into memory.
|
|
|
|
If "condition" is a list of numbers, then return files which have those positions in infolist.
|
|
|
|
If "condition" is a string, then it is treated as a wildcard for names of files to extract.
|
|
|
|
If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object
|
|
|
|
and returns boolean True (extract) or False (skip).
|
|
|
|
If "condition" is omitted, all files are returned.
|
|
|
|
|
|
|
|
Returns list of tuples (RarInfo info, str contents)
|
|
|
|
"""
|
|
|
|
checker = condition2checker(condition)
|
|
|
|
return RarFileImplementation.read_files(self, checker)
|
|
|
|
|
|
|
|
|
|
|
|
def extract(self, condition='*', path='.', withSubpath=True, overwrite=True):
|
|
|
|
"""Extract specific files from archive to disk.
|
|
|
|
|
|
|
|
If "condition" is a list of numbers, then extract files which have those positions in infolist.
|
|
|
|
If "condition" is a string, then it is treated as a wildcard for names of files to extract.
|
|
|
|
If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object
|
|
|
|
and returns either boolean True (extract) or boolean False (skip).
|
|
|
|
DEPRECATED: If "condition" callback returns string (only supported for Windows) -
|
|
|
|
that string will be used as a new name to save the file under.
|
|
|
|
If "condition" is omitted, all files are extracted.
|
|
|
|
|
|
|
|
"path" is a directory to extract to
|
|
|
|
"withSubpath" flag denotes whether files are extracted with their full path in the archive.
|
|
|
|
"overwrite" flag denotes whether extracted files will overwrite old ones. Defaults to true.
|
|
|
|
|
|
|
|
Returns list of RarInfos for extracted files."""
|
|
|
|
checker = condition2checker(condition)
|
|
|
|
return RarFileImplementation.extract(self, checker, path, withSubpath, overwrite)
|
|
|
|
|
|
|
|
def get_volume(self):
|
|
|
|
"""Determine which volume is it in a multi-volume archive. Returns None if it's not a
|
|
|
|
multi-volume archive, 0-based volume number otherwise."""
|
|
|
|
return RarFileImplementation.get_volume(self)
|
|
|
|
|
|
|
|
def condition2checker(condition):
|
|
|
|
"""Converts different condition types to callback"""
|
|
|
|
if type(condition) in [str, unicode]:
|
|
|
|
def smatcher(info):
|
|
|
|
return fnmatch.fnmatch(info.filename, condition)
|
|
|
|
return smatcher
|
|
|
|
elif type(condition) in [list, tuple] and type(condition[0]) in [int, long]:
|
|
|
|
def imatcher(info):
|
|
|
|
return info.index in condition
|
|
|
|
return imatcher
|
|
|
|
elif callable(condition):
|
|
|
|
return condition
|
|
|
|
else:
|
|
|
|
raise TypeError
|
|
|
|
|
|
|
|
|