diff --git a/src/libprs500/libunrar.py b/src/libprs500/libunrar.py new file mode 100644 index 0000000000..26140d0d1e --- /dev/null +++ b/src/libprs500/libunrar.py @@ -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') \ No newline at end of file