libunrar.py with support for extracting files from archive

This commit is contained in:
Kovid Goyal 2007-04-20 21:24:59 +00:00
parent 99e11d3693
commit bc4e05a417

190
src/libprs500/libunrar.py Normal file
View File

@ -0,0 +1,190 @@
## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
This module provides a thin ctypes based wrapper around libunrar.
See ftp://ftp.rarlabs.com/rar/unrarsrc-3.7.5.tar.gz
"""
from ctypes import Structure, c_char_p, c_uint, c_void_p, POINTER, \
byref, c_wchar_p, CFUNCTYPE, c_int, c_long, c_char, c_wchar
from StringIO import StringIO
from libprs500 import iswindows
_librar_name = 'libunrar.so'
if iswindows:
Structure._pack_ = 1
_librar_name = 'unrar'
from ctypes import windll
_libunrar = windll.LoadLibrary('unrar')
else:
from ctypes import cdll
_libunrar = cdll.LoadLibrary(_librar_name)
RAR_OM_LIST = 0
RAR_OM_EXTRACT = 1
ERAR_END_ARCHIVE = 10
ERAR_NO_MEMORY = 11
ERAR_BAD_DATA = 12
ERAR_BAD_ARCHIVE = 13
ERAR_UNKNOWN_FORMAT = 14
ERAR_EOPEN = 15
ERAR_ECREATE = 16
ERAR_ECLOSE = 17
ERAR_EREAD = 18
ERAR_EWRITE = 19
ERAR_SMALL_BUF = 20
ERAR_UNKNOWN = 21
ERAR_MISSING_PASSWORD = 22
RAR_VOL_ASK = 0
RAR_VOL_NOTIFY = 1
RAR_SKIP = 0
RAR_TEST = 1
RAR_EXTRACT = 2
class UnRARException(Exception):
pass
class RAROpenArchiveDataEx(Structure):
_fields_ = [
('ArcName', c_char_p),
('ArcNameW', c_wchar_p),
('OpenMode', c_uint),
('OpenResult', c_uint),
('CmtBuf', c_char_p),
('CmtBufSize', c_uint),
('CmtSize', c_uint),
('CmtState', c_uint),
('Flags', c_uint),
('Reserved', c_uint * 32)
]
class RARHeaderDataEx(Structure):
_fields_ = [
('ArcName', c_char*1024),
('ArcNameW', c_wchar*1024),
('FileName', c_char*1024),
('FileNameW', c_wchar*1024),
('Flags', c_uint),
('PackSize', c_uint),
('PackSizeHigh', c_uint),
('UnpSize', c_uint),
('UnpSizeHigh', c_uint),
('HostOS', c_uint),
('FileCRC', c_uint),
('FileTime', c_uint),
('UnpVer', c_uint),
('Method', c_uint),
('FileAttr', c_uint),
('CmtBuf', c_char_p),
('CmtBufSize', c_uint),
('CmtSize', c_uint),
('CmtState', c_uint),
('Reserved', c_uint*1024)
]
# Define a callback function
#CALLBACK_FUNC = CFUNCTYPE(c_int, c_uint, c_long, c_char_p, c_long)
#def py_callback_func(msg, user_data, p1, p2):
# return 0
#callback_func = CALLBACK_FUNC(py_callback_func)
_libunrar.RAROpenArchiveEx.argtypes = [POINTER(RAROpenArchiveDataEx)]
_libunrar.RAROpenArchiveEx.restype = c_void_p
_libunrar.RARReadHeaderEx.argtypes = [c_void_p, POINTER(RARHeaderDataEx)]
_libunrar.RARReadHeaderEx.restype = c_int
_libunrar.RARProcessFileW.argtypes = [c_void_p, c_int, c_wchar_p, c_wchar_p]
_libunrar.RARProcessFileW.restype = c_int
_libunrar.RARCloseArchive.argtypes = [c_void_p]
_libunrar.RARCloseArchive.restype = c_int
_libunrar.RARSetPassword.argtypes = [c_void_p, c_char_p]
#_libunrar.RARSetCallback.argtypes = [c_void_p, CALLBACK_FUNC, c_long]
def _interpret_open_error(code, path):
msg = 'Unknown error.'
if code == ERAR_NO_MEMORY:
msg = "Not enough memory to process " + path
elif code == ERAR_BAD_DATA:
msg = "Archive header broken: " + path
elif code == ERAR_BAD_ARCHIVE:
msg = path + ' is not a RAR archive.'
elif code == ERAR_EOPEN:
msg = 'Cannot open ' + path
return msg
def _interpret_process_file_error(code):
msg = 'Unknown Error'
if code == ERAR_UNKNOWN_FORMAT:
msg = 'Unknown archive format'
elif code == ERAR_BAD_ARCHIVE:
msg = 'Bad volume'
elif code == ERAR_ECREATE:
msg = 'File create error'
elif code == ERAR_EOPEN:
msg = 'Volume open error'
elif code == ERAR_ECLOSE:
msg = 'File close error'
elif code == ERAR_EREAD:
msg = 'Read error'
elif code == ERAR_EWRITE:
msg = 'Write error'
elif code == ERAR_BAD_DATA:
msg = 'CRC error'
elif code == ERAR_MISSING_PASSWORD:
msg = 'Password is required.'
return msg
def get_archive_info(flags):
ios = StringIO()
print >>ios, 'Volume:\t\t', 'yes' if (flags & 1) else 'no'
print >>ios, 'Comment:\t', 'yes' if (flags & 2) else 'no'
print >>ios, 'Locked:\t\t', 'yes' if (flags & 4) else 'no'
print >>ios, 'Solid:\t\t', 'yes' if (flags & 8) else 'no'
print >>ios, 'New naming:\t', 'yes' if (flags & 16) else 'no'
print >>ios, 'Authenticity:\t', 'yes' if (flags & 32) else 'no'
print >>ios, 'Recovery:\t', 'yes' if (flags & 64) else 'no'
print >>ios, 'Encr.headers:\t', 'yes' if (flags & 128) else 'no'
print >>ios, 'First Volume:\t', 'yes' if (flags & 256) else 'no or older than 3.0'
return ios.getvalue()
def extract(path):
open_archive_data = RAROpenArchiveDataEx(ArcName=path, OpenMode=RAR_OM_EXTRACT, CmtBuf=None)
arc_data = _libunrar.RAROpenArchiveEx(byref(open_archive_data))
try:
if open_archive_data.OpenResult != 0:
raise UnRARException(_interpret_open_error(open_archive_data.OpenResult, path))
print 'Archive:', path
print get_archive_info(open_archive_data.Flags)
header_data = RARHeaderDataEx(CmtBuf=None)
#_libunrar.RARSetCallback(arc_data, callback_func, mode)
while True:
RHCode = _libunrar.RARReadHeaderEx(arc_data, byref(header_data))
if RHCode != 0:
break
PFCode = _libunrar.RARProcessFileW(arc_data, RAR_EXTRACT, None, None)
if PFCode != 0:
raise UnRARException(_interpret_process_file_error(PFCode))
if RHCode == ERAR_BAD_DATA:
raise UnRARException('File header broken')
finally:
_libunrar.RARCloseArchive(arc_data)
extract(r'z:\home\test.rar')
#extract('/home/kovid/ero/Fansadox Collections/C21 Ponygirl Inferno.rar')