Release 1.2.0
This commit is contained in:
commit
57763c74ae
@ -1,18 +0,0 @@
|
|||||||
The unrar.dll library is freeware. This means:
|
|
||||||
|
|
||||||
1. All copyrights to RAR and the unrar.dll are exclusively
|
|
||||||
owned by the author - Alexander Roshal.
|
|
||||||
|
|
||||||
2. The unrar.dll library may be used in any software to handle RAR
|
|
||||||
archives without limitations free of charge.
|
|
||||||
|
|
||||||
3. THE RAR ARCHIVER AND THE UNRAR.DLL LIBRARY ARE DISTRIBUTED "AS IS".
|
|
||||||
NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT
|
|
||||||
YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS,
|
|
||||||
DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING
|
|
||||||
OR MISUSING THIS SOFTWARE.
|
|
||||||
|
|
||||||
Thank you for your interest in RAR and unrar.dll.
|
|
||||||
|
|
||||||
|
|
||||||
Alexander L. Roshal
|
|
Binary file not shown.
@ -1,140 +0,0 @@
|
|||||||
#ifndef _UNRAR_DLL_
|
|
||||||
#define _UNRAR_DLL_
|
|
||||||
|
|
||||||
#define ERAR_END_ARCHIVE 10
|
|
||||||
#define ERAR_NO_MEMORY 11
|
|
||||||
#define ERAR_BAD_DATA 12
|
|
||||||
#define ERAR_BAD_ARCHIVE 13
|
|
||||||
#define ERAR_UNKNOWN_FORMAT 14
|
|
||||||
#define ERAR_EOPEN 15
|
|
||||||
#define ERAR_ECREATE 16
|
|
||||||
#define ERAR_ECLOSE 17
|
|
||||||
#define ERAR_EREAD 18
|
|
||||||
#define ERAR_EWRITE 19
|
|
||||||
#define ERAR_SMALL_BUF 20
|
|
||||||
#define ERAR_UNKNOWN 21
|
|
||||||
#define ERAR_MISSING_PASSWORD 22
|
|
||||||
|
|
||||||
#define RAR_OM_LIST 0
|
|
||||||
#define RAR_OM_EXTRACT 1
|
|
||||||
#define RAR_OM_LIST_INCSPLIT 2
|
|
||||||
|
|
||||||
#define RAR_SKIP 0
|
|
||||||
#define RAR_TEST 1
|
|
||||||
#define RAR_EXTRACT 2
|
|
||||||
|
|
||||||
#define RAR_VOL_ASK 0
|
|
||||||
#define RAR_VOL_NOTIFY 1
|
|
||||||
|
|
||||||
#define RAR_DLL_VERSION 4
|
|
||||||
|
|
||||||
#ifdef _UNIX
|
|
||||||
#define CALLBACK
|
|
||||||
#define PASCAL
|
|
||||||
#define LONG long
|
|
||||||
#define HANDLE void *
|
|
||||||
#define LPARAM long
|
|
||||||
#define UINT unsigned int
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct RARHeaderData
|
|
||||||
{
|
|
||||||
char ArcName[260];
|
|
||||||
char FileName[260];
|
|
||||||
unsigned int Flags;
|
|
||||||
unsigned int PackSize;
|
|
||||||
unsigned int UnpSize;
|
|
||||||
unsigned int HostOS;
|
|
||||||
unsigned int FileCRC;
|
|
||||||
unsigned int FileTime;
|
|
||||||
unsigned int UnpVer;
|
|
||||||
unsigned int Method;
|
|
||||||
unsigned int FileAttr;
|
|
||||||
char *CmtBuf;
|
|
||||||
unsigned int CmtBufSize;
|
|
||||||
unsigned int CmtSize;
|
|
||||||
unsigned int CmtState;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct RARHeaderDataEx
|
|
||||||
{
|
|
||||||
char ArcName[1024];
|
|
||||||
wchar_t ArcNameW[1024];
|
|
||||||
char FileName[1024];
|
|
||||||
wchar_t FileNameW[1024];
|
|
||||||
unsigned int Flags;
|
|
||||||
unsigned int PackSize;
|
|
||||||
unsigned int PackSizeHigh;
|
|
||||||
unsigned int UnpSize;
|
|
||||||
unsigned int UnpSizeHigh;
|
|
||||||
unsigned int HostOS;
|
|
||||||
unsigned int FileCRC;
|
|
||||||
unsigned int FileTime;
|
|
||||||
unsigned int UnpVer;
|
|
||||||
unsigned int Method;
|
|
||||||
unsigned int FileAttr;
|
|
||||||
char *CmtBuf;
|
|
||||||
unsigned int CmtBufSize;
|
|
||||||
unsigned int CmtSize;
|
|
||||||
unsigned int CmtState;
|
|
||||||
unsigned int Reserved[1024];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct RAROpenArchiveData
|
|
||||||
{
|
|
||||||
char *ArcName;
|
|
||||||
unsigned int OpenMode;
|
|
||||||
unsigned int OpenResult;
|
|
||||||
char *CmtBuf;
|
|
||||||
unsigned int CmtBufSize;
|
|
||||||
unsigned int CmtSize;
|
|
||||||
unsigned int CmtState;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RAROpenArchiveDataEx
|
|
||||||
{
|
|
||||||
char *ArcName;
|
|
||||||
wchar_t *ArcNameW;
|
|
||||||
unsigned int OpenMode;
|
|
||||||
unsigned int OpenResult;
|
|
||||||
char *CmtBuf;
|
|
||||||
unsigned int CmtBufSize;
|
|
||||||
unsigned int CmtSize;
|
|
||||||
unsigned int CmtState;
|
|
||||||
unsigned int Flags;
|
|
||||||
unsigned int Reserved[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
enum UNRARCALLBACK_MESSAGES {
|
|
||||||
UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
|
|
||||||
|
|
||||||
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
|
|
||||||
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData);
|
|
||||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData);
|
|
||||||
int PASCAL RARCloseArchive(HANDLE hArcData);
|
|
||||||
int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData);
|
|
||||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData);
|
|
||||||
int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName);
|
|
||||||
int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName);
|
|
||||||
void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData);
|
|
||||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc);
|
|
||||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc);
|
|
||||||
void PASCAL RARSetPassword(HANDLE hArcData,char *Password);
|
|
||||||
int PASCAL RARGetDllVersion();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
Binary file not shown.
@ -1,606 +0,0 @@
|
|||||||
|
|
||||||
UnRAR.dll Manual
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
UnRAR.dll is a 32-bit Windows dynamic-link library which provides
|
|
||||||
file extraction from RAR archives.
|
|
||||||
|
|
||||||
|
|
||||||
Exported functions
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData)
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Open RAR archive and allocate memory structures
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
ArchiveData Points to RAROpenArchiveData structure
|
|
||||||
|
|
||||||
struct RAROpenArchiveData
|
|
||||||
{
|
|
||||||
char *ArcName;
|
|
||||||
UINT OpenMode;
|
|
||||||
UINT OpenResult;
|
|
||||||
char *CmtBuf;
|
|
||||||
UINT CmtBufSize;
|
|
||||||
UINT CmtSize;
|
|
||||||
UINT CmtState;
|
|
||||||
};
|
|
||||||
|
|
||||||
Structure fields:
|
|
||||||
|
|
||||||
ArcName
|
|
||||||
Input parameter which should point to zero terminated string
|
|
||||||
containing the archive name.
|
|
||||||
|
|
||||||
OpenMode
|
|
||||||
Input parameter.
|
|
||||||
|
|
||||||
Possible values
|
|
||||||
|
|
||||||
RAR_OM_LIST
|
|
||||||
Open archive for reading file headers only.
|
|
||||||
|
|
||||||
RAR_OM_EXTRACT
|
|
||||||
Open archive for testing and extracting files.
|
|
||||||
|
|
||||||
RAR_OM_LIST_INCSPLIT
|
|
||||||
Open archive for reading file headers only. If you open an archive
|
|
||||||
in such mode, RARReadHeader[Ex] will return all file headers,
|
|
||||||
including those with "file continued from previous volume" flag.
|
|
||||||
In case of RAR_OM_LIST such headers are automatically skipped.
|
|
||||||
So if you process RAR volumes in RAR_OM_LIST_INCSPLIT mode, you will
|
|
||||||
get several file header records for same file if file is split between
|
|
||||||
volumes. For such files only the last file header record will contain
|
|
||||||
the correct file CRC and if you wish to get the correct packed size,
|
|
||||||
you need to sum up packed sizes of all parts.
|
|
||||||
|
|
||||||
OpenResult
|
|
||||||
Output parameter.
|
|
||||||
|
|
||||||
Possible values
|
|
||||||
|
|
||||||
0 Success
|
|
||||||
ERAR_NO_MEMORY Not enough memory to initialize data structures
|
|
||||||
ERAR_BAD_DATA Archive header broken
|
|
||||||
ERAR_BAD_ARCHIVE File is not valid RAR archive
|
|
||||||
ERAR_UNKNOWN_FORMAT Unknown encryption used for archive headers
|
|
||||||
ERAR_EOPEN File open error
|
|
||||||
|
|
||||||
CmtBuf
|
|
||||||
Input parameter which should point to the buffer for archive
|
|
||||||
comments. Maximum comment size is limited to 64Kb. Comment text is
|
|
||||||
zero terminated. If the comment text is larger than the buffer
|
|
||||||
size, the comment text will be truncated. If CmtBuf is set to
|
|
||||||
NULL, comments will not be read.
|
|
||||||
|
|
||||||
CmtBufSize
|
|
||||||
Input parameter which should contain size of buffer for archive
|
|
||||||
comments.
|
|
||||||
|
|
||||||
CmtSize
|
|
||||||
Output parameter containing size of comments actually read into the
|
|
||||||
buffer, cannot exceed CmtBufSize.
|
|
||||||
|
|
||||||
CmtState
|
|
||||||
Output parameter.
|
|
||||||
|
|
||||||
Possible values
|
|
||||||
|
|
||||||
0 comments not present
|
|
||||||
1 Comments read completely
|
|
||||||
ERAR_NO_MEMORY Not enough memory to extract comments
|
|
||||||
ERAR_BAD_DATA Broken comment
|
|
||||||
ERAR_UNKNOWN_FORMAT Unknown comment format
|
|
||||||
ERAR_SMALL_BUF Buffer too small, comments not completely read
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
Archive handle or NULL in case of error
|
|
||||||
|
|
||||||
|
|
||||||
========================================================================
|
|
||||||
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData)
|
|
||||||
========================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Similar to RAROpenArchive, but uses RAROpenArchiveDataEx structure
|
|
||||||
allowing to specify Unicode archive name and returning information
|
|
||||||
about archive flags.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
ArchiveData Points to RAROpenArchiveDataEx structure
|
|
||||||
|
|
||||||
struct RAROpenArchiveDataEx
|
|
||||||
{
|
|
||||||
char *ArcName;
|
|
||||||
wchar_t *ArcNameW;
|
|
||||||
unsigned int OpenMode;
|
|
||||||
unsigned int OpenResult;
|
|
||||||
char *CmtBuf;
|
|
||||||
unsigned int CmtBufSize;
|
|
||||||
unsigned int CmtSize;
|
|
||||||
unsigned int CmtState;
|
|
||||||
unsigned int Flags;
|
|
||||||
unsigned int Reserved[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
Structure fields:
|
|
||||||
|
|
||||||
ArcNameW
|
|
||||||
Input parameter which should point to zero terminated Unicode string
|
|
||||||
containing the archive name or NULL if Unicode name is not specified.
|
|
||||||
|
|
||||||
Flags
|
|
||||||
Output parameter. Combination of bit flags.
|
|
||||||
|
|
||||||
Possible values
|
|
||||||
|
|
||||||
0x0001 - Volume attribute (archive volume)
|
|
||||||
0x0002 - Archive comment present
|
|
||||||
0x0004 - Archive lock attribute
|
|
||||||
0x0008 - Solid attribute (solid archive)
|
|
||||||
0x0010 - New volume naming scheme ('volname.partN.rar')
|
|
||||||
0x0020 - Authenticity information present
|
|
||||||
0x0040 - Recovery record present
|
|
||||||
0x0080 - Block headers are encrypted
|
|
||||||
0x0100 - First volume (set only by RAR 3.0 and later)
|
|
||||||
|
|
||||||
Reserved[32]
|
|
||||||
Reserved for future use. Must be zero.
|
|
||||||
|
|
||||||
Information on other structure fields and function return values
|
|
||||||
is available above, in RAROpenArchive function description.
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
int PASCAL RARCloseArchive(HANDLE hArcData)
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Close RAR archive and release allocated memory. It must be called when
|
|
||||||
archive processing is finished, even if the archive processing was stopped
|
|
||||||
due to an error.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
hArcData
|
|
||||||
This parameter should contain the archive handle obtained from the
|
|
||||||
RAROpenArchive function call.
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
0 Success
|
|
||||||
ERAR_ECLOSE Archive close error
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
int PASCAL RARReadHeader(HANDLE hArcData,
|
|
||||||
struct RARHeaderData *HeaderData)
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Read header of file in archive.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
hArcData
|
|
||||||
This parameter should contain the archive handle obtained from the
|
|
||||||
RAROpenArchive function call.
|
|
||||||
|
|
||||||
HeaderData
|
|
||||||
It should point to RARHeaderData structure:
|
|
||||||
|
|
||||||
struct RARHeaderData
|
|
||||||
{
|
|
||||||
char ArcName[260];
|
|
||||||
char FileName[260];
|
|
||||||
UINT Flags;
|
|
||||||
UINT PackSize;
|
|
||||||
UINT UnpSize;
|
|
||||||
UINT HostOS;
|
|
||||||
UINT FileCRC;
|
|
||||||
UINT FileTime;
|
|
||||||
UINT UnpVer;
|
|
||||||
UINT Method;
|
|
||||||
UINT FileAttr;
|
|
||||||
char *CmtBuf;
|
|
||||||
UINT CmtBufSize;
|
|
||||||
UINT CmtSize;
|
|
||||||
UINT CmtState;
|
|
||||||
};
|
|
||||||
|
|
||||||
Structure fields:
|
|
||||||
|
|
||||||
ArcName
|
|
||||||
Output parameter which contains a zero terminated string of the
|
|
||||||
current archive name. May be used to determine the current volume
|
|
||||||
name.
|
|
||||||
|
|
||||||
FileName
|
|
||||||
Output parameter which contains a zero terminated string of the
|
|
||||||
file name in OEM (DOS) encoding.
|
|
||||||
|
|
||||||
Flags
|
|
||||||
Output parameter which contains file flags:
|
|
||||||
|
|
||||||
0x01 - file continued from previous volume
|
|
||||||
0x02 - file continued on next volume
|
|
||||||
0x04 - file encrypted with password
|
|
||||||
0x08 - file comment present
|
|
||||||
0x10 - compression of previous files is used (solid flag)
|
|
||||||
|
|
||||||
bits 7 6 5
|
|
||||||
|
|
||||||
0 0 0 - dictionary size 64 Kb
|
|
||||||
0 0 1 - dictionary size 128 Kb
|
|
||||||
0 1 0 - dictionary size 256 Kb
|
|
||||||
0 1 1 - dictionary size 512 Kb
|
|
||||||
1 0 0 - dictionary size 1024 Kb
|
|
||||||
1 0 1 - dictionary size 2048 KB
|
|
||||||
1 1 0 - dictionary size 4096 KB
|
|
||||||
1 1 1 - file is directory
|
|
||||||
|
|
||||||
Other bits are reserved.
|
|
||||||
|
|
||||||
PackSize
|
|
||||||
Output parameter means packed file size or size of the
|
|
||||||
file part if file was split between volumes.
|
|
||||||
|
|
||||||
UnpSize
|
|
||||||
Output parameter - unpacked file size.
|
|
||||||
|
|
||||||
HostOS
|
|
||||||
Output parameter - operating system used for archiving:
|
|
||||||
|
|
||||||
0 - MS DOS;
|
|
||||||
1 - OS/2.
|
|
||||||
2 - Win32
|
|
||||||
3 - Unix
|
|
||||||
|
|
||||||
FileCRC
|
|
||||||
Output parameter which contains unpacked file CRC. In case of file parts
|
|
||||||
split between volumes only the last part contains the correct CRC
|
|
||||||
and it is accessible only in RAR_OM_LIST_INCSPLIT listing mode.
|
|
||||||
|
|
||||||
FileTime
|
|
||||||
Output parameter - contains date and time in standard MS DOS format.
|
|
||||||
|
|
||||||
UnpVer
|
|
||||||
Output parameter - RAR version needed to extract file.
|
|
||||||
It is encoded as 10 * Major version + minor version.
|
|
||||||
|
|
||||||
Method
|
|
||||||
Output parameter - packing method.
|
|
||||||
|
|
||||||
FileAttr
|
|
||||||
Output parameter - file attributes.
|
|
||||||
|
|
||||||
CmtBuf
|
|
||||||
File comments support is not implemented in the new DLL version yet.
|
|
||||||
Now CmtState is always 0.
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Input parameter which should point to the buffer for file
|
|
||||||
* comments. Maximum comment size is limited to 64Kb. Comment text is
|
|
||||||
* a zero terminated string in OEM encoding. If the comment text is
|
|
||||||
* larger than the buffer size, the comment text will be truncated.
|
|
||||||
* If CmtBuf is set to NULL, comments will not be read.
|
|
||||||
*/
|
|
||||||
|
|
||||||
CmtBufSize
|
|
||||||
Input parameter which should contain size of buffer for archive
|
|
||||||
comments.
|
|
||||||
|
|
||||||
CmtSize
|
|
||||||
Output parameter containing size of comments actually read into the
|
|
||||||
buffer, should not exceed CmtBufSize.
|
|
||||||
|
|
||||||
CmtState
|
|
||||||
Output parameter.
|
|
||||||
|
|
||||||
Possible values
|
|
||||||
|
|
||||||
0 Absent comments
|
|
||||||
1 Comments read completely
|
|
||||||
ERAR_NO_MEMORY Not enough memory to extract comments
|
|
||||||
ERAR_BAD_DATA Broken comment
|
|
||||||
ERAR_UNKNOWN_FORMAT Unknown comment format
|
|
||||||
ERAR_SMALL_BUF Buffer too small, comments not completely read
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
0 Success
|
|
||||||
ERAR_END_ARCHIVE End of archive
|
|
||||||
ERAR_BAD_DATA File header broken
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
int PASCAL RARReadHeaderEx(HANDLE hArcData,
|
|
||||||
struct RARHeaderDataEx *HeaderData)
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Similar to RARReadHeader, but uses RARHeaderDataEx structure,
|
|
||||||
containing information about Unicode file names and 64 bit file sizes.
|
|
||||||
|
|
||||||
struct RARHeaderDataEx
|
|
||||||
{
|
|
||||||
char ArcName[1024];
|
|
||||||
wchar_t ArcNameW[1024];
|
|
||||||
char FileName[1024];
|
|
||||||
wchar_t FileNameW[1024];
|
|
||||||
unsigned int Flags;
|
|
||||||
unsigned int PackSize;
|
|
||||||
unsigned int PackSizeHigh;
|
|
||||||
unsigned int UnpSize;
|
|
||||||
unsigned int UnpSizeHigh;
|
|
||||||
unsigned int HostOS;
|
|
||||||
unsigned int FileCRC;
|
|
||||||
unsigned int FileTime;
|
|
||||||
unsigned int UnpVer;
|
|
||||||
unsigned int Method;
|
|
||||||
unsigned int FileAttr;
|
|
||||||
char *CmtBuf;
|
|
||||||
unsigned int CmtBufSize;
|
|
||||||
unsigned int CmtSize;
|
|
||||||
unsigned int CmtState;
|
|
||||||
unsigned int Reserved[1024];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
int PASCAL RARProcessFile(HANDLE hArcData,
|
|
||||||
int Operation,
|
|
||||||
char *DestPath,
|
|
||||||
char *DestName)
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Performs action and moves the current position in the archive to
|
|
||||||
the next file. Extract or test the current file from the archive
|
|
||||||
opened in RAR_OM_EXTRACT mode. If the mode RAR_OM_LIST is set,
|
|
||||||
then a call to this function will simply skip the archive position
|
|
||||||
to the next file.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
hArcData
|
|
||||||
This parameter should contain the archive handle obtained from the
|
|
||||||
RAROpenArchive function call.
|
|
||||||
|
|
||||||
Operation
|
|
||||||
File operation.
|
|
||||||
|
|
||||||
Possible values
|
|
||||||
|
|
||||||
RAR_SKIP Move to the next file in the archive. If the
|
|
||||||
archive is solid and RAR_OM_EXTRACT mode was set
|
|
||||||
when the archive was opened, the current file will
|
|
||||||
be processed - the operation will be performed
|
|
||||||
slower than a simple seek.
|
|
||||||
|
|
||||||
RAR_TEST Test the current file and move to the next file in
|
|
||||||
the archive. If the archive was opened with
|
|
||||||
RAR_OM_LIST mode, the operation is equal to
|
|
||||||
RAR_SKIP.
|
|
||||||
|
|
||||||
RAR_EXTRACT Extract the current file and move to the next file.
|
|
||||||
If the archive was opened with RAR_OM_LIST mode,
|
|
||||||
the operation is equal to RAR_SKIP.
|
|
||||||
|
|
||||||
|
|
||||||
DestPath
|
|
||||||
This parameter should point to a zero terminated string containing the
|
|
||||||
destination directory to which to extract files to. If DestPath is equal
|
|
||||||
to NULL, it means extract to the current directory. This parameter has
|
|
||||||
meaning only if DestName is NULL.
|
|
||||||
|
|
||||||
DestName
|
|
||||||
This parameter should point to a string containing the full path and name
|
|
||||||
to assign to extracted file or it can be NULL to use the default name.
|
|
||||||
If DestName is defined (not NULL), it overrides both the original file
|
|
||||||
name saved in the archive and path specigied in DestPath setting.
|
|
||||||
|
|
||||||
Both DestPath and DestName must be in OEM encoding. If necessary,
|
|
||||||
use CharToOem to convert text to OEM before passing to this function.
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
0 Success
|
|
||||||
ERAR_BAD_DATA File CRC error
|
|
||||||
ERAR_BAD_ARCHIVE Volume is not valid RAR archive
|
|
||||||
ERAR_UNKNOWN_FORMAT Unknown archive format
|
|
||||||
ERAR_EOPEN Volume open error
|
|
||||||
ERAR_ECREATE File create error
|
|
||||||
ERAR_ECLOSE File close error
|
|
||||||
ERAR_EREAD Read error
|
|
||||||
ERAR_EWRITE Write error
|
|
||||||
|
|
||||||
|
|
||||||
Note: if you wish to cancel extraction, return -1 when processing
|
|
||||||
UCM_PROCESSDATA callback message.
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
int PASCAL RARProcessFileW(HANDLE hArcData,
|
|
||||||
int Operation,
|
|
||||||
wchar_t *DestPath,
|
|
||||||
wchar_t *DestName)
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Unicode version of RARProcessFile. It uses Unicode DestPath
|
|
||||||
and DestName parameters, other parameters and return values
|
|
||||||
are the same as in RARProcessFile.
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
void PASCAL RARSetCallback(HANDLE hArcData,
|
|
||||||
int PASCAL (*CallbackProc)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2),
|
|
||||||
LPARAM UserData);
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Set a user-defined callback function to process Unrar events.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
hArcData
|
|
||||||
This parameter should contain the archive handle obtained from the
|
|
||||||
RAROpenArchive function call.
|
|
||||||
|
|
||||||
CallbackProc
|
|
||||||
It should point to a user-defined callback function.
|
|
||||||
|
|
||||||
The function will be passed four parameters:
|
|
||||||
|
|
||||||
|
|
||||||
msg Type of event. Described below.
|
|
||||||
|
|
||||||
UserData User defined value passed to RARSetCallback.
|
|
||||||
|
|
||||||
P1 and P2 Event dependent parameters. Described below.
|
|
||||||
|
|
||||||
|
|
||||||
Possible events
|
|
||||||
|
|
||||||
UCM_CHANGEVOLUME Process volume change.
|
|
||||||
|
|
||||||
P1 Points to the zero terminated name
|
|
||||||
of the next volume.
|
|
||||||
|
|
||||||
P2 The function call mode:
|
|
||||||
|
|
||||||
RAR_VOL_ASK Required volume is absent. The function should
|
|
||||||
prompt user and return a positive value
|
|
||||||
to retry or return -1 value to terminate
|
|
||||||
operation. The function may also specify a new
|
|
||||||
volume name, placing it to the address specified
|
|
||||||
by P1 parameter.
|
|
||||||
|
|
||||||
RAR_VOL_NOTIFY Required volume is successfully opened.
|
|
||||||
This is a notification call and volume name
|
|
||||||
modification is not allowed. The function should
|
|
||||||
return a positive value to continue or -1
|
|
||||||
to terminate operation.
|
|
||||||
|
|
||||||
UCM_PROCESSDATA Process unpacked data. It may be used to read
|
|
||||||
a file while it is being extracted or tested
|
|
||||||
without actual extracting file to disk.
|
|
||||||
Return a positive value to continue process
|
|
||||||
or -1 to cancel the archive operation
|
|
||||||
|
|
||||||
P1 Address pointing to the unpacked data.
|
|
||||||
Function may refer to the data but must not
|
|
||||||
change it.
|
|
||||||
|
|
||||||
P2 Size of the unpacked data. It is guaranteed
|
|
||||||
only that the size will not exceed the maximum
|
|
||||||
dictionary size (4 Mb in RAR 3.0).
|
|
||||||
|
|
||||||
UCM_NEEDPASSWORD DLL needs a password to process archive.
|
|
||||||
This message must be processed if you wish
|
|
||||||
to be able to handle archives with encrypted
|
|
||||||
file names. It can be also used as replacement
|
|
||||||
of RARSetPassword function even for usual
|
|
||||||
encrypted files with non-encrypted names.
|
|
||||||
|
|
||||||
P1 Address pointing to the buffer for a password.
|
|
||||||
You need to copy a password here.
|
|
||||||
|
|
||||||
P2 Size of the password buffer.
|
|
||||||
|
|
||||||
|
|
||||||
UserData
|
|
||||||
User data passed to callback function.
|
|
||||||
|
|
||||||
Other functions of UnRAR.dll should not be called from the callback
|
|
||||||
function.
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
void PASCAL RARSetChangeVolProc(HANDLE hArcData,
|
|
||||||
int PASCAL (*ChangeVolProc)(char *ArcName,int Mode));
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Obsoleted, use RARSetCallback instead.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
void PASCAL RARSetProcessDataProc(HANDLE hArcData,
|
|
||||||
int PASCAL (*ProcessDataProc)(unsigned char *Addr,int Size))
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Obsoleted, use RARSetCallback instead.
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
void PASCAL RARSetPassword(HANDLE hArcData,
|
|
||||||
char *Password);
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Set a password to decrypt files.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
hArcData
|
|
||||||
This parameter should contain the archive handle obtained from the
|
|
||||||
RAROpenArchive function call.
|
|
||||||
|
|
||||||
Password
|
|
||||||
It should point to a string containing a zero terminated password.
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
None
|
|
||||||
|
|
||||||
|
|
||||||
====================================================================
|
|
||||||
void PASCAL RARGetDllVersion();
|
|
||||||
====================================================================
|
|
||||||
|
|
||||||
Description
|
|
||||||
~~~~~~~~~~~
|
|
||||||
Returns API version.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
~~~~~~~~~~
|
|
||||||
None.
|
|
||||||
|
|
||||||
Return values
|
|
||||||
~~~~~~~~~~~~~
|
|
||||||
Returns an integer value denoting UnRAR.dll API version, which is also
|
|
||||||
defined in unrar.h as RAR_DLL_VERSION. API version number is incremented
|
|
||||||
only in case of noticeable changes in UnRAR.dll API. Do not confuse it
|
|
||||||
with version of UnRAR.dll stored in DLL resources, which is incremented
|
|
||||||
with every DLL rebuild.
|
|
||||||
|
|
||||||
If RARGetDllVersion() returns a value lower than UnRAR.dll which your
|
|
||||||
application was designed for, it may indicate that DLL version is too old
|
|
||||||
and it will fail to provide all necessary functions to your application.
|
|
||||||
|
|
||||||
This function is absent in old versions of UnRAR.dll, so it is safer
|
|
||||||
to use LoadLibrary and GetProcAddress to access this function.
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
|||||||
List of unrar.dll API changes. We do not include performance and reliability
|
|
||||||
improvements into this list, but this library and RAR/UnRAR tools share
|
|
||||||
the same source code. So the latest version of unrar.dll usually contains
|
|
||||||
same decompression algorithm changes as the latest UnRAR version.
|
|
||||||
============================================================================
|
|
||||||
|
|
||||||
-- 18 January 2008
|
|
||||||
|
|
||||||
all LONG parameters of CallbackProc function were changed
|
|
||||||
to LPARAM type for 64 bit mode compatibility.
|
|
||||||
|
|
||||||
|
|
||||||
-- 12 December 2007
|
|
||||||
|
|
||||||
Added new RAR_OM_LIST_INCSPLIT open mode for function RAROpenArchive.
|
|
||||||
|
|
||||||
|
|
||||||
-- 14 August 2007
|
|
||||||
|
|
||||||
Added NoCrypt\unrar_nocrypt.dll without decryption code for those
|
|
||||||
applications where presence of encryption or decryption code is not
|
|
||||||
allowed because of legal restrictions.
|
|
||||||
|
|
||||||
|
|
||||||
-- 14 December 2006
|
|
||||||
|
|
||||||
Added ERAR_MISSING_PASSWORD error type. This error is returned
|
|
||||||
if empty password is specified for encrypted file.
|
|
||||||
|
|
||||||
|
|
||||||
-- 12 June 2003
|
|
||||||
|
|
||||||
Added RARProcessFileW function, Unicode version of RARProcessFile
|
|
||||||
|
|
||||||
|
|
||||||
-- 9 August 2002
|
|
||||||
|
|
||||||
Added RAROpenArchiveEx function allowing to specify Unicode archive
|
|
||||||
name and get archive flags.
|
|
||||||
|
|
||||||
|
|
||||||
-- 24 January 2002
|
|
||||||
|
|
||||||
Added RARReadHeaderEx function allowing to read Unicode file names
|
|
||||||
and 64 bit file sizes.
|
|
||||||
|
|
||||||
|
|
||||||
-- 23 January 2002
|
|
||||||
|
|
||||||
Added ERAR_UNKNOWN error type (it is used for all errors which
|
|
||||||
do not have special ERAR code yet) and UCM_NEEDPASSWORD callback
|
|
||||||
message.
|
|
||||||
|
|
||||||
Unrar.dll automatically opens all next volumes not only when extracting,
|
|
||||||
but also in RAR_OM_LIST mode.
|
|
||||||
|
|
||||||
|
|
||||||
-- 27 November 2001
|
|
||||||
|
|
||||||
RARSetChangeVolProc and RARSetProcessDataProc are replaced by
|
|
||||||
the single callback function installed with RARSetCallback.
|
|
||||||
Unlike old style callbacks, the new function accepts the user defined
|
|
||||||
parameter. Unrar.dll still supports RARSetChangeVolProc and
|
|
||||||
RARSetProcessDataProc for compatibility purposes, but if you write
|
|
||||||
a new application, better use RARSetCallback.
|
|
||||||
|
|
||||||
File comments support is not implemented in the new DLL version yet.
|
|
||||||
Now CmtState is always 0.
|
|
||||||
|
|
||||||
|
|
||||||
-- 13 August 2001
|
|
||||||
|
|
||||||
Added RARGetDllVersion function, so you may distinguish old unrar.dll,
|
|
||||||
which used C style callback functions and the new one with PASCAL callbacks.
|
|
||||||
|
|
||||||
|
|
||||||
-- 10 May 2001
|
|
||||||
|
|
||||||
Callback functions in RARSetChangeVolProc and RARSetProcessDataProc
|
|
||||||
use PASCAL style call convention now.
|
|
@ -1 +0,0 @@
|
|||||||
This is x64 version of unrar.dll.
|
|
Binary file not shown.
Binary file not shown.
@ -1,177 +0,0 @@
|
|||||||
# 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.3'
|
|
||||||
|
|
||||||
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 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
|
|
||||||
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
# Low level interface - see UnRARDLL\UNRARDLL.TXT
|
|
||||||
|
|
||||||
|
|
||||||
class ArchiveHeaderBroken(Exception): pass
|
|
||||||
class InvalidRARArchive(Exception): pass
|
|
||||||
class FileOpenError(Exception): pass
|
|
||||||
class IncorrectRARPassword(Exception): pass
|
|
||||||
class InvalidRARArchiveUsage(Exception): pass
|
|
@ -1,138 +0,0 @@
|
|||||||
import os, sys
|
|
||||||
|
|
||||||
import UnRAR2
|
|
||||||
from UnRAR2.rar_exceptions import *
|
|
||||||
|
|
||||||
|
|
||||||
def cleanup(dir='test'):
|
|
||||||
for path, dirs, files in os.walk(dir):
|
|
||||||
for fn in files:
|
|
||||||
os.remove(os.path.join(path, fn))
|
|
||||||
for dir in dirs:
|
|
||||||
os.removedirs(os.path.join(path, dir))
|
|
||||||
|
|
||||||
|
|
||||||
# basic test
|
|
||||||
cleanup()
|
|
||||||
rarc = UnRAR2.RarFile('test.rar')
|
|
||||||
rarc.infolist()
|
|
||||||
assert rarc.comment == "This is a test."
|
|
||||||
for info in rarc.infoiter():
|
|
||||||
saveinfo = info
|
|
||||||
assert (str(info)=="""<RarInfo "test" in "test.rar">""")
|
|
||||||
break
|
|
||||||
rarc.extract()
|
|
||||||
assert os.path.exists('test'+os.sep+'test.txt')
|
|
||||||
assert os.path.exists('test'+os.sep+'this.py')
|
|
||||||
del rarc
|
|
||||||
assert (str(saveinfo)=="""<RarInfo "test" in "[ARCHIVE_NO_LONGER_LOADED]">""")
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
# extract all the files in test.rar
|
|
||||||
cleanup()
|
|
||||||
UnRAR2.RarFile('test.rar').extract()
|
|
||||||
assert os.path.exists('test'+os.sep+'test.txt')
|
|
||||||
assert os.path.exists('test'+os.sep+'this.py')
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
# extract all the files in test.rar matching the wildcard *.txt
|
|
||||||
cleanup()
|
|
||||||
UnRAR2.RarFile('test.rar').extract('*.txt')
|
|
||||||
assert os.path.exists('test'+os.sep+'test.txt')
|
|
||||||
assert not os.path.exists('test'+os.sep+'this.py')
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
|
|
||||||
# check the name and size of each file, extracting small ones
|
|
||||||
cleanup()
|
|
||||||
archive = UnRAR2.RarFile('test.rar')
|
|
||||||
assert archive.comment == 'This is a test.'
|
|
||||||
archive.extract(lambda rarinfo: rarinfo.size <= 1024)
|
|
||||||
for rarinfo in archive.infoiter():
|
|
||||||
if rarinfo.size <= 1024 and not rarinfo.isdir:
|
|
||||||
assert rarinfo.size == os.stat(rarinfo.filename).st_size
|
|
||||||
assert file('test'+os.sep+'test.txt', 'rt').read() == 'This is only a test.'
|
|
||||||
assert not os.path.exists('test'+os.sep+'this.py')
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
|
|
||||||
# extract this.py, overriding it's destination
|
|
||||||
cleanup('test2')
|
|
||||||
archive = UnRAR2.RarFile('test.rar')
|
|
||||||
archive.extract('*.py', 'test2', False)
|
|
||||||
assert os.path.exists('test2'+os.sep+'this.py')
|
|
||||||
cleanup('test2')
|
|
||||||
|
|
||||||
|
|
||||||
# extract test.txt to memory
|
|
||||||
cleanup()
|
|
||||||
archive = UnRAR2.RarFile('test.rar')
|
|
||||||
entries = UnRAR2.RarFile('test.rar').read_files('*test.txt')
|
|
||||||
assert len(entries)==1
|
|
||||||
assert entries[0][0].filename.endswith('test.txt')
|
|
||||||
assert entries[0][1]=='This is only a test.'
|
|
||||||
|
|
||||||
|
|
||||||
# extract all the files in test.rar with overwriting
|
|
||||||
cleanup()
|
|
||||||
fo = open('test'+os.sep+'test.txt',"wt")
|
|
||||||
fo.write("blah")
|
|
||||||
fo.close()
|
|
||||||
UnRAR2.RarFile('test.rar').extract('*.txt')
|
|
||||||
assert open('test'+os.sep+'test.txt',"rt").read()!="blah"
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
# extract all the files in test.rar without overwriting
|
|
||||||
cleanup()
|
|
||||||
fo = open('test'+os.sep+'test.txt',"wt")
|
|
||||||
fo.write("blahblah")
|
|
||||||
fo.close()
|
|
||||||
UnRAR2.RarFile('test.rar').extract('*.txt', overwrite = False)
|
|
||||||
assert open('test'+os.sep+'test.txt',"rt").read()=="blahblah"
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
# list big file in an archive
|
|
||||||
list(UnRAR2.RarFile('test_nulls.rar').infoiter())
|
|
||||||
|
|
||||||
# extract files from an archive with protected files
|
|
||||||
cleanup()
|
|
||||||
rarc = UnRAR2.RarFile('test_protected_files.rar', password="protected")
|
|
||||||
rarc.extract()
|
|
||||||
assert os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
|
|
||||||
cleanup()
|
|
||||||
errored = False
|
|
||||||
try:
|
|
||||||
UnRAR2.RarFile('test_protected_files.rar', password="proteqted").extract()
|
|
||||||
except IncorrectRARPassword:
|
|
||||||
errored = True
|
|
||||||
assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
|
|
||||||
assert errored
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
# extract files from an archive with protected headers
|
|
||||||
cleanup()
|
|
||||||
UnRAR2.RarFile('test_protected_headers.rar', password="secret").extract()
|
|
||||||
assert os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
|
|
||||||
cleanup()
|
|
||||||
errored = False
|
|
||||||
try:
|
|
||||||
UnRAR2.RarFile('test_protected_headers.rar', password="seqret").extract()
|
|
||||||
except IncorrectRARPassword:
|
|
||||||
errored = True
|
|
||||||
assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
|
|
||||||
assert errored
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
# make sure docstring examples are working
|
|
||||||
import doctest
|
|
||||||
doctest.testmod(UnRAR2)
|
|
||||||
|
|
||||||
# update documentation
|
|
||||||
import pydoc
|
|
||||||
pydoc.writedoc(UnRAR2)
|
|
||||||
|
|
||||||
# cleanup
|
|
||||||
try:
|
|
||||||
os.remove('__init__.pyc')
|
|
||||||
except:
|
|
||||||
pass
|
|
@ -1,218 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
# Unix version uses unrar command line executable
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import gc
|
|
||||||
|
|
||||||
import os, os.path
|
|
||||||
import time, re
|
|
||||||
|
|
||||||
from rar_exceptions import *
|
|
||||||
|
|
||||||
class UnpackerNotInstalled(Exception): pass
|
|
||||||
|
|
||||||
rar_executable_cached = None
|
|
||||||
rar_executable_version = None
|
|
||||||
|
|
||||||
def call_unrar(params):
|
|
||||||
"Calls rar/unrar command line executable, returns stdout pipe"
|
|
||||||
global rar_executable_cached
|
|
||||||
if rar_executable_cached is None:
|
|
||||||
for command in ('unrar', 'rar'):
|
|
||||||
try:
|
|
||||||
subprocess.Popen([command], stdout=subprocess.PIPE)
|
|
||||||
rar_executable_cached = command
|
|
||||||
break
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
if rar_executable_cached is None:
|
|
||||||
raise UnpackerNotInstalled("No suitable RAR unpacker installed")
|
|
||||||
|
|
||||||
assert type(params) == list, "params must be list"
|
|
||||||
args = [rar_executable_cached] + params
|
|
||||||
try:
|
|
||||||
gc.disable() # See http://bugs.python.org/issue1336
|
|
||||||
return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
finally:
|
|
||||||
gc.enable()
|
|
||||||
|
|
||||||
class RarFileImplementation(object):
|
|
||||||
|
|
||||||
def init(self, password=None):
|
|
||||||
global rar_executable_version
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
|
|
||||||
stdoutdata, stderrdata = self.call('v', []).communicate()
|
|
||||||
|
|
||||||
for line in stderrdata.splitlines():
|
|
||||||
if line.strip().startswith("Cannot open"):
|
|
||||||
raise FileOpenError
|
|
||||||
if line.find("CRC failed")>=0:
|
|
||||||
raise IncorrectRARPassword
|
|
||||||
accum = []
|
|
||||||
source = iter(stdoutdata.splitlines())
|
|
||||||
line = ''
|
|
||||||
while not (line.startswith('UNRAR')):
|
|
||||||
line = source.next()
|
|
||||||
signature = line
|
|
||||||
# The code below is mighty flaky
|
|
||||||
# and will probably crash on localized versions of RAR
|
|
||||||
# but I see no safe way to rewrite it using a CLI tool
|
|
||||||
if signature.startswith("UNRAR 4"):
|
|
||||||
rar_executable_version = 4
|
|
||||||
while not (line.startswith('Comment:') or line.startswith('Pathname/Comment')):
|
|
||||||
if line.strip().endswith('is not RAR archive'):
|
|
||||||
raise InvalidRARArchive
|
|
||||||
line = source.next()
|
|
||||||
while not line.startswith('Pathname/Comment'):
|
|
||||||
accum.append(line.rstrip('\n'))
|
|
||||||
line = source.next()
|
|
||||||
if len(accum):
|
|
||||||
accum[0] = accum[0][9:] # strip out "Comment:" part
|
|
||||||
self.comment = '\n'.join(accum[:-1])
|
|
||||||
else:
|
|
||||||
self.comment = None
|
|
||||||
elif signature.startswith("UNRAR 5"):
|
|
||||||
rar_executable_version = 5
|
|
||||||
line = source.next()
|
|
||||||
while not line.startswith('Archive:'):
|
|
||||||
if line.strip().endswith('is not RAR archive'):
|
|
||||||
raise InvalidRARArchive
|
|
||||||
accum.append(line.rstrip('\n'))
|
|
||||||
line = source.next()
|
|
||||||
if len(accum):
|
|
||||||
self.comment = '\n'.join(accum[:-1]).strip()
|
|
||||||
else:
|
|
||||||
self.comment = None
|
|
||||||
else:
|
|
||||||
raise UnpackerNotInstalled("Unsupported RAR version, expected 4.x or 5.x, found: "
|
|
||||||
+ signature.split(" ")[1])
|
|
||||||
|
|
||||||
|
|
||||||
def escaped_password(self):
|
|
||||||
return '-' if self.password == None else self.password
|
|
||||||
|
|
||||||
|
|
||||||
def call(self, cmd, options=[], files=[]):
|
|
||||||
options2 = options + ['p'+self.escaped_password()]
|
|
||||||
soptions = ['-'+x for x in options2]
|
|
||||||
return call_unrar([cmd]+soptions+['--',self.archiveName]+files)
|
|
||||||
|
|
||||||
def infoiter(self):
|
|
||||||
|
|
||||||
command = "v" if rar_executable_version == 4 else "l"
|
|
||||||
stdoutdata, stderrdata = self.call(command, ['c-']).communicate()
|
|
||||||
|
|
||||||
for line in stderrdata.splitlines():
|
|
||||||
if line.strip().startswith("Cannot open"):
|
|
||||||
raise FileOpenError
|
|
||||||
|
|
||||||
accum = []
|
|
||||||
source = iter(stdoutdata.splitlines())
|
|
||||||
line = ''
|
|
||||||
while not line.startswith('-----------'):
|
|
||||||
if line.strip().endswith('is not RAR archive'):
|
|
||||||
raise InvalidRARArchive
|
|
||||||
if line.startswith("CRC failed") or line.startswith("Checksum error"):
|
|
||||||
raise IncorrectRARPassword
|
|
||||||
line = source.next()
|
|
||||||
line = source.next()
|
|
||||||
i = 0
|
|
||||||
re_spaces = re.compile(r"\s+")
|
|
||||||
if rar_executable_version == 4:
|
|
||||||
while not line.startswith('-----------'):
|
|
||||||
accum.append(line)
|
|
||||||
if len(accum)==2:
|
|
||||||
data = {}
|
|
||||||
data['index'] = i
|
|
||||||
# asterisks mark password-encrypted files
|
|
||||||
data['filename'] = accum[0].strip().lstrip("*") # asterisks marks password-encrypted files
|
|
||||||
fields = re_spaces.split(accum[1].strip())
|
|
||||||
data['size'] = int(fields[0])
|
|
||||||
attr = fields[5]
|
|
||||||
data['isdir'] = 'd' in attr.lower()
|
|
||||||
data['datetime'] = time.strptime(fields[3]+" "+fields[4], '%d-%m-%y %H:%M')
|
|
||||||
data['comment'] = None
|
|
||||||
yield data
|
|
||||||
accum = []
|
|
||||||
i += 1
|
|
||||||
line = source.next()
|
|
||||||
elif rar_executable_version == 5:
|
|
||||||
while not line.startswith('-----------'):
|
|
||||||
fields = line.strip().lstrip("*").split()
|
|
||||||
data = {}
|
|
||||||
data['index'] = i
|
|
||||||
data['filename'] = " ".join(fields[4:])
|
|
||||||
data['size'] = int(fields[1])
|
|
||||||
attr = fields[0]
|
|
||||||
data['isdir'] = 'd' in attr.lower()
|
|
||||||
data['datetime'] = time.strptime(fields[2]+" "+fields[3], '%d-%m-%y %H:%M')
|
|
||||||
data['comment'] = None
|
|
||||||
yield data
|
|
||||||
i += 1
|
|
||||||
line = source.next()
|
|
||||||
|
|
||||||
|
|
||||||
def read_files(self, checker):
|
|
||||||
res = []
|
|
||||||
for info in self.infoiter():
|
|
||||||
checkres = checker(info)
|
|
||||||
if checkres==True and not info.isdir:
|
|
||||||
pipe = self.call('p', ['inul'], [info.filename]).stdout
|
|
||||||
res.append((info, pipe.read()))
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def extract(self, checker, path, withSubpath, overwrite):
|
|
||||||
res = []
|
|
||||||
command = 'x'
|
|
||||||
if not withSubpath:
|
|
||||||
command = 'e'
|
|
||||||
options = []
|
|
||||||
if overwrite:
|
|
||||||
options.append('o+')
|
|
||||||
else:
|
|
||||||
options.append('o-')
|
|
||||||
if not path.endswith(os.sep):
|
|
||||||
path += os.sep
|
|
||||||
names = []
|
|
||||||
for info in self.infoiter():
|
|
||||||
checkres = checker(info)
|
|
||||||
if type(checkres) in [str, unicode]:
|
|
||||||
raise NotImplementedError("Condition callbacks returning strings are deprecated and only supported in Windows")
|
|
||||||
if checkres==True and not info.isdir:
|
|
||||||
names.append(info.filename)
|
|
||||||
res.append(info)
|
|
||||||
names.append(path)
|
|
||||||
proc = self.call(command, options, names)
|
|
||||||
stdoutdata, stderrdata = proc.communicate()
|
|
||||||
if stderrdata.find("CRC failed")>=0 or stderrdata.find("Checksum error")>=0:
|
|
||||||
raise IncorrectRARPassword
|
|
||||||
return res
|
|
||||||
|
|
||||||
def destruct(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
@ -1,309 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
# Low level interface - see UnRARDLL\UNRARDLL.TXT
|
|
||||||
|
|
||||||
from __future__ import generators
|
|
||||||
|
|
||||||
import ctypes, ctypes.wintypes
|
|
||||||
import os, os.path, sys
|
|
||||||
import Queue
|
|
||||||
import time
|
|
||||||
|
|
||||||
from rar_exceptions import *
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
RAR_OM_LIST = 0
|
|
||||||
RAR_OM_EXTRACT = 1
|
|
||||||
|
|
||||||
RAR_SKIP = 0
|
|
||||||
RAR_TEST = 1
|
|
||||||
RAR_EXTRACT = 2
|
|
||||||
|
|
||||||
RAR_VOL_ASK = 0
|
|
||||||
RAR_VOL_NOTIFY = 1
|
|
||||||
|
|
||||||
RAR_DLL_VERSION = 3
|
|
||||||
|
|
||||||
# enum UNRARCALLBACK_MESSAGES
|
|
||||||
UCM_CHANGEVOLUME = 0
|
|
||||||
UCM_PROCESSDATA = 1
|
|
||||||
UCM_NEEDPASSWORD = 2
|
|
||||||
|
|
||||||
architecture_bits = ctypes.sizeof(ctypes.c_voidp)*8
|
|
||||||
dll_name = "unrar.dll"
|
|
||||||
if architecture_bits == 64:
|
|
||||||
dll_name = "x64\\unrar64.dll"
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
unrar = ctypes.WinDLL(os.path.join(os.path.split(__file__)[0], 'UnRARDLL', dll_name))
|
|
||||||
except WindowsError:
|
|
||||||
unrar = ctypes.WinDLL(dll_name)
|
|
||||||
|
|
||||||
|
|
||||||
class RAROpenArchiveDataEx(ctypes.Structure):
|
|
||||||
def __init__(self, ArcName=None, ArcNameW=u'', OpenMode=RAR_OM_LIST):
|
|
||||||
self.CmtBuf = ctypes.c_buffer(64*1024)
|
|
||||||
ctypes.Structure.__init__(self, ArcName=ArcName, ArcNameW=ArcNameW, OpenMode=OpenMode, _CmtBuf=ctypes.addressof(self.CmtBuf), CmtBufSize=ctypes.sizeof(self.CmtBuf))
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
('ArcName', ctypes.c_char_p),
|
|
||||||
('ArcNameW', ctypes.c_wchar_p),
|
|
||||||
('OpenMode', ctypes.c_uint),
|
|
||||||
('OpenResult', ctypes.c_uint),
|
|
||||||
('_CmtBuf', ctypes.c_voidp),
|
|
||||||
('CmtBufSize', ctypes.c_uint),
|
|
||||||
('CmtSize', ctypes.c_uint),
|
|
||||||
('CmtState', ctypes.c_uint),
|
|
||||||
('Flags', ctypes.c_uint),
|
|
||||||
('Reserved', ctypes.c_uint*32),
|
|
||||||
]
|
|
||||||
|
|
||||||
class RARHeaderDataEx(ctypes.Structure):
|
|
||||||
def __init__(self):
|
|
||||||
self.CmtBuf = ctypes.c_buffer(64*1024)
|
|
||||||
ctypes.Structure.__init__(self, _CmtBuf=ctypes.addressof(self.CmtBuf), CmtBufSize=ctypes.sizeof(self.CmtBuf))
|
|
||||||
|
|
||||||
_fields_ = [
|
|
||||||
('ArcName', ctypes.c_char*1024),
|
|
||||||
('ArcNameW', ctypes.c_wchar*1024),
|
|
||||||
('FileName', ctypes.c_char*1024),
|
|
||||||
('FileNameW', ctypes.c_wchar*1024),
|
|
||||||
('Flags', ctypes.c_uint),
|
|
||||||
('PackSize', ctypes.c_uint),
|
|
||||||
('PackSizeHigh', ctypes.c_uint),
|
|
||||||
('UnpSize', ctypes.c_uint),
|
|
||||||
('UnpSizeHigh', ctypes.c_uint),
|
|
||||||
('HostOS', ctypes.c_uint),
|
|
||||||
('FileCRC', ctypes.c_uint),
|
|
||||||
('FileTime', ctypes.c_uint),
|
|
||||||
('UnpVer', ctypes.c_uint),
|
|
||||||
('Method', ctypes.c_uint),
|
|
||||||
('FileAttr', ctypes.c_uint),
|
|
||||||
('_CmtBuf', ctypes.c_voidp),
|
|
||||||
('CmtBufSize', ctypes.c_uint),
|
|
||||||
('CmtSize', ctypes.c_uint),
|
|
||||||
('CmtState', ctypes.c_uint),
|
|
||||||
('Reserved', ctypes.c_uint*1024),
|
|
||||||
]
|
|
||||||
|
|
||||||
def DosDateTimeToTimeTuple(dosDateTime):
|
|
||||||
"""Convert an MS-DOS format date time to a Python time tuple.
|
|
||||||
"""
|
|
||||||
dosDate = dosDateTime >> 16
|
|
||||||
dosTime = dosDateTime & 0xffff
|
|
||||||
day = dosDate & 0x1f
|
|
||||||
month = (dosDate >> 5) & 0xf
|
|
||||||
year = 1980 + (dosDate >> 9)
|
|
||||||
second = 2*(dosTime & 0x1f)
|
|
||||||
minute = (dosTime >> 5) & 0x3f
|
|
||||||
hour = dosTime >> 11
|
|
||||||
return time.localtime(time.mktime((year, month, day, hour, minute, second, 0, 1, -1)))
|
|
||||||
|
|
||||||
def _wrap(restype, function, argtypes):
|
|
||||||
result = function
|
|
||||||
result.argtypes = argtypes
|
|
||||||
result.restype = restype
|
|
||||||
return result
|
|
||||||
|
|
||||||
RARGetDllVersion = _wrap(ctypes.c_int, unrar.RARGetDllVersion, [])
|
|
||||||
|
|
||||||
RAROpenArchiveEx = _wrap(ctypes.wintypes.HANDLE, unrar.RAROpenArchiveEx, [ctypes.POINTER(RAROpenArchiveDataEx)])
|
|
||||||
|
|
||||||
RARReadHeaderEx = _wrap(ctypes.c_int, unrar.RARReadHeaderEx, [ctypes.wintypes.HANDLE, ctypes.POINTER(RARHeaderDataEx)])
|
|
||||||
|
|
||||||
_RARSetPassword = _wrap(ctypes.c_int, unrar.RARSetPassword, [ctypes.wintypes.HANDLE, ctypes.c_char_p])
|
|
||||||
def RARSetPassword(*args, **kwargs):
|
|
||||||
_RARSetPassword(*args, **kwargs)
|
|
||||||
|
|
||||||
RARProcessFile = _wrap(ctypes.c_int, unrar.RARProcessFile, [ctypes.wintypes.HANDLE, ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p])
|
|
||||||
|
|
||||||
RARCloseArchive = _wrap(ctypes.c_int, unrar.RARCloseArchive, [ctypes.wintypes.HANDLE])
|
|
||||||
|
|
||||||
UNRARCALLBACK = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_uint, ctypes.c_long, ctypes.c_long, ctypes.c_long)
|
|
||||||
RARSetCallback = _wrap(ctypes.c_int, unrar.RARSetCallback, [ctypes.wintypes.HANDLE, UNRARCALLBACK, ctypes.c_long])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
RARExceptions = {
|
|
||||||
ERAR_NO_MEMORY : MemoryError,
|
|
||||||
ERAR_BAD_DATA : ArchiveHeaderBroken,
|
|
||||||
ERAR_BAD_ARCHIVE : InvalidRARArchive,
|
|
||||||
ERAR_EOPEN : FileOpenError,
|
|
||||||
}
|
|
||||||
|
|
||||||
class PassiveReader:
|
|
||||||
"""Used for reading files to memory"""
|
|
||||||
def __init__(self, usercallback = None):
|
|
||||||
self.buf = []
|
|
||||||
self.ucb = usercallback
|
|
||||||
|
|
||||||
def _callback(self, msg, UserData, P1, P2):
|
|
||||||
if msg == UCM_PROCESSDATA:
|
|
||||||
data = (ctypes.c_char*P2).from_address(P1).raw
|
|
||||||
if self.ucb!=None:
|
|
||||||
self.ucb(data)
|
|
||||||
else:
|
|
||||||
self.buf.append(data)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def get_result(self):
|
|
||||||
return ''.join(self.buf)
|
|
||||||
|
|
||||||
class RarInfoIterator(object):
|
|
||||||
def __init__(self, arc):
|
|
||||||
self.arc = arc
|
|
||||||
self.index = 0
|
|
||||||
self.headerData = RARHeaderDataEx()
|
|
||||||
self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData))
|
|
||||||
if self.res==ERAR_BAD_DATA:
|
|
||||||
raise IncorrectRARPassword
|
|
||||||
self.arc.lockStatus = "locked"
|
|
||||||
self.arc.needskip = False
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
if self.index>0:
|
|
||||||
if self.arc.needskip:
|
|
||||||
RARProcessFile(self.arc._handle, RAR_SKIP, None, None)
|
|
||||||
self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData))
|
|
||||||
|
|
||||||
if self.res:
|
|
||||||
raise StopIteration
|
|
||||||
self.arc.needskip = True
|
|
||||||
|
|
||||||
data = {}
|
|
||||||
data['index'] = self.index
|
|
||||||
data['filename'] = self.headerData.FileName
|
|
||||||
data['datetime'] = DosDateTimeToTimeTuple(self.headerData.FileTime)
|
|
||||||
data['isdir'] = ((self.headerData.Flags & 0xE0) == 0xE0)
|
|
||||||
data['size'] = self.headerData.UnpSize + (self.headerData.UnpSizeHigh << 32)
|
|
||||||
if self.headerData.CmtState == 1:
|
|
||||||
data['comment'] = self.headerData.CmtBuf.value
|
|
||||||
else:
|
|
||||||
data['comment'] = None
|
|
||||||
self.index += 1
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.arc.lockStatus = "finished"
|
|
||||||
|
|
||||||
def generate_password_provider(password):
|
|
||||||
def password_provider_callback(msg, UserData, P1, P2):
|
|
||||||
if msg == UCM_NEEDPASSWORD and password!=None:
|
|
||||||
(ctypes.c_char*P2).from_address(P1).value = password
|
|
||||||
return 1
|
|
||||||
return password_provider_callback
|
|
||||||
|
|
||||||
class RarFileImplementation(object):
|
|
||||||
|
|
||||||
def init(self, password=None):
|
|
||||||
self.password = password
|
|
||||||
archiveData = RAROpenArchiveDataEx(ArcNameW=self.archiveName, OpenMode=RAR_OM_EXTRACT)
|
|
||||||
self._handle = RAROpenArchiveEx(ctypes.byref(archiveData))
|
|
||||||
self.c_callback = UNRARCALLBACK(generate_password_provider(self.password))
|
|
||||||
RARSetCallback(self._handle, self.c_callback, 1)
|
|
||||||
|
|
||||||
if archiveData.OpenResult != 0:
|
|
||||||
raise RARExceptions[archiveData.OpenResult]
|
|
||||||
|
|
||||||
if archiveData.CmtState == 1:
|
|
||||||
self.comment = archiveData.CmtBuf.value
|
|
||||||
else:
|
|
||||||
self.comment = None
|
|
||||||
|
|
||||||
if password:
|
|
||||||
RARSetPassword(self._handle, password)
|
|
||||||
|
|
||||||
self.lockStatus = "ready"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def destruct(self):
|
|
||||||
if self._handle and RARCloseArchive:
|
|
||||||
RARCloseArchive(self._handle)
|
|
||||||
|
|
||||||
def make_sure_ready(self):
|
|
||||||
if self.lockStatus == "locked":
|
|
||||||
raise InvalidRARArchiveUsage("cannot execute infoiter() without finishing previous one")
|
|
||||||
if self.lockStatus == "finished":
|
|
||||||
self.destruct()
|
|
||||||
self.init(self.password)
|
|
||||||
|
|
||||||
def infoiter(self):
|
|
||||||
self.make_sure_ready()
|
|
||||||
return RarInfoIterator(self)
|
|
||||||
|
|
||||||
def read_files(self, checker):
|
|
||||||
res = []
|
|
||||||
for info in self.infoiter():
|
|
||||||
if checker(info) and not info.isdir:
|
|
||||||
reader = PassiveReader()
|
|
||||||
c_callback = UNRARCALLBACK(reader._callback)
|
|
||||||
RARSetCallback(self._handle, c_callback, 1)
|
|
||||||
tmpres = RARProcessFile(self._handle, RAR_TEST, None, None)
|
|
||||||
if tmpres==ERAR_BAD_DATA:
|
|
||||||
raise IncorrectRARPassword
|
|
||||||
self.needskip = False
|
|
||||||
res.append((info, reader.get_result()))
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def extract(self, checker, path, withSubpath, overwrite):
|
|
||||||
res = []
|
|
||||||
for info in self.infoiter():
|
|
||||||
checkres = checker(info)
|
|
||||||
if checkres!=False and not info.isdir:
|
|
||||||
if checkres==True:
|
|
||||||
fn = info.filename
|
|
||||||
if not withSubpath:
|
|
||||||
fn = os.path.split(fn)[-1]
|
|
||||||
target = os.path.join(path, fn)
|
|
||||||
else:
|
|
||||||
raise DeprecationWarning, "Condition callbacks returning strings are deprecated and only supported in Windows"
|
|
||||||
target = checkres
|
|
||||||
if overwrite or (not os.path.exists(target)):
|
|
||||||
tmpres = RARProcessFile(self._handle, RAR_EXTRACT, None, target)
|
|
||||||
if tmpres==ERAR_BAD_DATA:
|
|
||||||
raise IncorrectRARPassword
|
|
||||||
|
|
||||||
self.needskip = False
|
|
||||||
res.append(info)
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
@ -19,8 +19,8 @@ import xml.etree.ElementTree as ET
|
|||||||
#from pprint import pprint
|
#from pprint import pprint
|
||||||
#import zipfile
|
#import zipfile
|
||||||
|
|
||||||
from genericmetadata import GenericMetadata
|
from .genericmetadata import GenericMetadata
|
||||||
import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
class CoMet:
|
class CoMet:
|
||||||
@ -76,7 +76,7 @@ class CoMet:
|
|||||||
# helper func
|
# helper func
|
||||||
def assign(comet_entry, md_entry):
|
def assign(comet_entry, md_entry):
|
||||||
if md_entry is not None:
|
if md_entry is not None:
|
||||||
ET.SubElement(root, comet_entry).text = u"{0}".format(md_entry)
|
ET.SubElement(root, comet_entry).text = "{0}".format(md_entry)
|
||||||
|
|
||||||
# title is manditory
|
# title is manditory
|
||||||
if md.title is None:
|
if md.title is None:
|
||||||
@ -131,43 +131,43 @@ class CoMet:
|
|||||||
if credit['role'].lower() in set(self.writer_synonyms):
|
if credit['role'].lower() in set(self.writer_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'writer').text = u"{0}".format(
|
'writer').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
if credit['role'].lower() in set(self.penciller_synonyms):
|
if credit['role'].lower() in set(self.penciller_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'penciller').text = u"{0}".format(
|
'penciller').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
if credit['role'].lower() in set(self.inker_synonyms):
|
if credit['role'].lower() in set(self.inker_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'inker').text = u"{0}".format(
|
'inker').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
if credit['role'].lower() in set(self.colorist_synonyms):
|
if credit['role'].lower() in set(self.colorist_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'colorist').text = u"{0}".format(
|
'colorist').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
if credit['role'].lower() in set(self.letterer_synonyms):
|
if credit['role'].lower() in set(self.letterer_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'letterer').text = u"{0}".format(
|
'letterer').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
if credit['role'].lower() in set(self.cover_synonyms):
|
if credit['role'].lower() in set(self.cover_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'coverDesigner').text = u"{0}".format(
|
'coverDesigner').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
if credit['role'].lower() in set(self.editor_synonyms):
|
if credit['role'].lower() in set(self.editor_synonyms):
|
||||||
ET.SubElement(
|
ET.SubElement(
|
||||||
root,
|
root,
|
||||||
'editor').text = u"{0}".format(
|
'editor').text = "{0}".format(
|
||||||
credit['person'])
|
credit['person'])
|
||||||
|
|
||||||
# self pretty-print
|
# self pretty-print
|
||||||
|
@ -23,21 +23,55 @@ import subprocess
|
|||||||
import platform
|
import platform
|
||||||
import ctypes
|
import ctypes
|
||||||
import time
|
import time
|
||||||
import StringIO
|
import io
|
||||||
#import io
|
#import io
|
||||||
#import locale
|
#import locale
|
||||||
#import shutil
|
#import shutil
|
||||||
|
|
||||||
from natsort import natsorted
|
from natsort import natsorted
|
||||||
from PyPDF2 import PdfFileReader
|
from PyPDF2 import PdfFileReader
|
||||||
|
try:
|
||||||
from unrar import rarfile
|
from unrar import rarfile
|
||||||
from unrar import unrarlib
|
from unrar import unrarlib
|
||||||
from unrar import constants
|
from unrar import constants
|
||||||
#import UnRAR2
|
# monkey patch unrarlib to avoid segfaults on Win10
|
||||||
#from UnRAR2.rar_exceptions import *
|
if platform.system() == 'Windows':
|
||||||
|
unrarlib.UNRARCALLBACK = ctypes.WINFUNCTYPE(
|
||||||
|
# return type
|
||||||
|
ctypes.c_int,
|
||||||
|
# msg
|
||||||
|
ctypes.c_uint,
|
||||||
|
# UserData
|
||||||
|
ctypes.c_long,
|
||||||
|
# MONKEY PATCH HERE -- use a pointer instead of a long, in unrar code: (LPARAM)(*byte),
|
||||||
|
# that is a pointer to byte casted to LPARAM
|
||||||
|
# On win10 64bit causes nasty segfaults when used from pyinstaller
|
||||||
|
ctypes.POINTER(ctypes.c_byte),
|
||||||
|
# size
|
||||||
|
ctypes.c_long
|
||||||
|
)
|
||||||
|
RARSetCallback = unrarlib._c_func(unrarlib.RARSetCallback, None,
|
||||||
|
[unrarlib.HANDLE, unrarlib.UNRARCALLBACK, ctypes.c_long])
|
||||||
|
def _rar_cb(self, msg, user_data, p1, p2):
|
||||||
|
if (msg == constants.UCM_NEEDPASSWORD or
|
||||||
|
msg == constants.UCM_NEEDPASSWORDW):
|
||||||
|
# This is a work around since libunrar doesn't
|
||||||
|
# properly return the error code when files are encrypted
|
||||||
|
self._missing_password = True
|
||||||
|
elif msg == constants.UCM_PROCESSDATA:
|
||||||
|
if self._data is None:
|
||||||
|
self._data = b''
|
||||||
|
chunk = ctypes.string_at(p1, p2)
|
||||||
|
self._data += chunk
|
||||||
|
return 1
|
||||||
|
rarfile._ReadIntoMemory._callback = _rar_cb
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print("WARNING: cannot find libunrar, rar support is disabled")
|
||||||
|
pass
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
#if platform.system() == "Windows":
|
||||||
import _subprocess
|
# import _subprocess
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import Image
|
import Image
|
||||||
@ -45,11 +79,11 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pil_available = False
|
pil_available = False
|
||||||
|
|
||||||
from comicinfoxml import ComicInfoXml
|
from .comicinfoxml import ComicInfoXml
|
||||||
from comicbookinfo import ComicBookInfo
|
from .comicbookinfo import ComicBookInfo
|
||||||
from comet import CoMet
|
from .comet import CoMet
|
||||||
from genericmetadata import GenericMetadata, PageType
|
from .genericmetadata import GenericMetadata, PageType
|
||||||
from filenameparser import FileNameParser
|
from .filenameparser import FileNameParser
|
||||||
#from settings import ComicTaggerSettings
|
#from settings import ComicTaggerSettings
|
||||||
|
|
||||||
|
|
||||||
@ -76,7 +110,10 @@ class ZipArchiver:
|
|||||||
return comment
|
return comment
|
||||||
|
|
||||||
def setArchiveComment(self, comment):
|
def setArchiveComment(self, comment):
|
||||||
return self.writeZipComment(self.path, comment)
|
zf = zipfile.ZipFile(self.path, 'a')
|
||||||
|
zf.comment = bytes(comment, 'utf-8')
|
||||||
|
zf.close()
|
||||||
|
return True
|
||||||
|
|
||||||
def readArchiveFile(self, archive_file):
|
def readArchiveFile(self, archive_file):
|
||||||
data = ""
|
data = ""
|
||||||
@ -85,14 +122,13 @@ class ZipArchiver:
|
|||||||
try:
|
try:
|
||||||
data = zf.read(archive_file)
|
data = zf.read(archive_file)
|
||||||
except zipfile.BadZipfile as e:
|
except zipfile.BadZipfile as e:
|
||||||
print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(
|
print("bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file), file=sys.stderr)
|
||||||
e, self.path, archive_file)
|
|
||||||
zf.close()
|
zf.close()
|
||||||
raise IOError
|
raise IOError
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
zf.close()
|
zf.close()
|
||||||
print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(
|
print("bad zipfile [{0}]: {1} :: {2}".format(
|
||||||
e, self.path, archive_file)
|
e, self.path, archive_file), file=sys.stderr)
|
||||||
raise IOError
|
raise IOError
|
||||||
finally:
|
finally:
|
||||||
zf.close()
|
zf.close()
|
||||||
@ -117,6 +153,7 @@ class ZipArchiver:
|
|||||||
zf = zipfile.ZipFile(
|
zf = zipfile.ZipFile(
|
||||||
self.path,
|
self.path,
|
||||||
mode='a',
|
mode='a',
|
||||||
|
allowZip64=True,
|
||||||
compression=zipfile.ZIP_DEFLATED)
|
compression=zipfile.ZIP_DEFLATED)
|
||||||
zf.writestr(archive_file, data)
|
zf.writestr(archive_file, data)
|
||||||
zf.close()
|
zf.close()
|
||||||
@ -131,8 +168,8 @@ class ZipArchiver:
|
|||||||
zf.close()
|
zf.close()
|
||||||
return namelist
|
return namelist
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print >> sys.stderr, u"Unable to get zipfile list [{0}]: {1}".format(
|
print("Unable to get zipfile list [{0}]: {1}".format(
|
||||||
e, self.path)
|
e, self.path), file=sys.stderr)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def rebuildZipFile(self, exclude_list):
|
def rebuildZipFile(self, exclude_list):
|
||||||
@ -149,7 +186,7 @@ class ZipArchiver:
|
|||||||
os.close(tmp_fd)
|
os.close(tmp_fd)
|
||||||
|
|
||||||
zin = zipfile.ZipFile(self.path, 'r')
|
zin = zipfile.ZipFile(self.path, 'r')
|
||||||
zout = zipfile.ZipFile(tmp_name, 'w')
|
zout = zipfile.ZipFile(tmp_name, 'w', allowZip64=True)
|
||||||
for item in zin.infolist():
|
for item in zin.infolist():
|
||||||
buffer = zin.read(item.filename)
|
buffer = zin.read(item.filename)
|
||||||
if (item.filename not in exclude_list):
|
if (item.filename not in exclude_list):
|
||||||
@ -220,12 +257,12 @@ class ZipArchiver:
|
|||||||
fo.seek(pos + 2, 2)
|
fo.seek(pos + 2, 2)
|
||||||
|
|
||||||
# write out the comment itself
|
# write out the comment itself
|
||||||
fo.write(comment)
|
fo.write(bytes(comment))
|
||||||
fo.truncate()
|
fo.truncate()
|
||||||
fo.close()
|
fo.close()
|
||||||
else:
|
else:
|
||||||
raise Exception('Failed to write comment to zip file!')
|
raise Exception('Failed to write comment to zip file!')
|
||||||
except:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
@ -234,7 +271,7 @@ class ZipArchiver:
|
|||||||
"""Replace the current zip with one copied from another archive"""
|
"""Replace the current zip with one copied from another archive"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zout = zipfile.ZipFile(self.path, 'w')
|
zout = zipfile.ZipFile(self.path, 'w', allowZip64=True)
|
||||||
for fname in otherArchive.getArchiveFilenameList():
|
for fname in otherArchive.getArchiveFilenameList():
|
||||||
data = otherArchive.readArchiveFile(fname)
|
data = otherArchive.readArchiveFile(fname)
|
||||||
if data is not None:
|
if data is not None:
|
||||||
@ -247,8 +284,8 @@ class ZipArchiver:
|
|||||||
if not self.writeZipComment(self.path, comment):
|
if not self.writeZipComment(self.path, comment):
|
||||||
return False
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print >> sys.stderr, u"Error while copying to {0}: {1}".format(
|
print("Error while copying to {0}: {1}".format(
|
||||||
self.path, e)
|
self.path, e), file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
@ -272,7 +309,7 @@ class RarArchiver:
|
|||||||
# windows only, keeps the cmd.exe from popping up
|
# windows only, keeps the cmd.exe from popping up
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
self.startupinfo = subprocess.STARTUPINFO()
|
self.startupinfo = subprocess.STARTUPINFO()
|
||||||
self.startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
|
self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||||
else:
|
else:
|
||||||
self.startupinfo = None
|
self.startupinfo = None
|
||||||
|
|
||||||
@ -281,37 +318,38 @@ class RarArchiver:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def getArchiveComment(self):
|
def getArchiveComment(self):
|
||||||
|
|
||||||
rarc = self.getRARObj()
|
rarc = self.getRARObj()
|
||||||
return rarc.comment
|
return rarc.comment
|
||||||
|
|
||||||
def setArchiveComment(self, comment):
|
def setArchiveComment(self, comment):
|
||||||
|
|
||||||
if self.rar_exe_path is not None:
|
if self.rar_exe_path is not None:
|
||||||
try:
|
try:
|
||||||
# write comment to temp file
|
# write comment to temp file
|
||||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||||
f = os.fdopen(tmp_fd, 'w+b')
|
f = os.fdopen(tmp_fd, 'w+')
|
||||||
f.write(comment)
|
f.write(comment)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
working_dir = os.path.dirname(os.path.abspath(self.path))
|
working_dir = os.path.dirname(os.path.abspath(self.path))
|
||||||
|
|
||||||
# use external program to write comment to Rar archive
|
# use external program to write comment to Rar archive
|
||||||
subprocess.call([self.rar_exe_path,
|
proc_args = [self.rar_exe_path,
|
||||||
'c',
|
'c',
|
||||||
'-w' + working_dir,
|
'-w' + working_dir,
|
||||||
'-c-',
|
'-c-',
|
||||||
'-z' + tmp_name,
|
'-z' + tmp_name,
|
||||||
self.path],
|
self.path]
|
||||||
|
subprocess.call(proc_args,
|
||||||
startupinfo=self.startupinfo,
|
startupinfo=self.startupinfo,
|
||||||
stdout=RarArchiver.devnull)
|
stdout=RarArchiver.devnull,
|
||||||
|
stdin=RarArchiver.devnull,
|
||||||
|
stderr=RarArchiver.devnull)
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
os.remove(tmp_name)
|
os.remove(tmp_name)
|
||||||
except:
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
@ -343,26 +381,26 @@ class RarArchiver:
|
|||||||
#entries = rarc.read_files( archive_file )
|
#entries = rarc.read_files( archive_file )
|
||||||
|
|
||||||
if entries[0][0].file_size != len(entries[0][1]):
|
if entries[0][0].file_size != len(entries[0][1]):
|
||||||
print >> sys.stderr, u"readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
|
print("readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
|
||||||
entries[0][0].file_size, len(
|
entries[0][0].file_size, len(
|
||||||
entries[0][1]), self.path, archive_file, tries)
|
entries[0][1]), self.path, archive_file, tries), file=sys.stderr)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
print >> sys.stderr, u"readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(
|
print("readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(
|
||||||
str(e), self.path, archive_file, tries)
|
str(e), self.path, archive_file, tries), file=sys.stderr)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print >> sys.stderr, u"Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(
|
print("Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(
|
||||||
str(e), self.path, archive_file, tries)
|
str(e), self.path, archive_file, tries), file=sys.stderr)
|
||||||
break
|
break
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Success"
|
# Success"
|
||||||
# entries is a list of of tuples: ( rarinfo, filedata)
|
# entries is a list of of tuples: ( rarinfo, filedata)
|
||||||
if tries > 1:
|
if tries > 1:
|
||||||
print >> sys.stderr, u"Attempted read_files() {0} times".format(
|
print("Attempted read_files() {0} times".format(
|
||||||
tries)
|
tries), file=sys.stderr)
|
||||||
if (len(entries) == 1):
|
if (len(entries) == 1):
|
||||||
return entries[0][1]
|
return entries[0][1]
|
||||||
else:
|
else:
|
||||||
@ -395,7 +433,9 @@ class RarArchiver:
|
|||||||
self.path,
|
self.path,
|
||||||
tmp_file],
|
tmp_file],
|
||||||
startupinfo=self.startupinfo,
|
startupinfo=self.startupinfo,
|
||||||
stdout=RarArchiver.devnull)
|
stdout=RarArchiver.devnull,
|
||||||
|
stdin=RarArchiver.devnull,
|
||||||
|
stderr=RarArchiver.devnull)
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@ -418,7 +458,9 @@ class RarArchiver:
|
|||||||
self.path,
|
self.path,
|
||||||
archive_file],
|
archive_file],
|
||||||
startupinfo=self.startupinfo,
|
startupinfo=self.startupinfo,
|
||||||
stdout=RarArchiver.devnull)
|
stdout=RarArchiver.devnull,
|
||||||
|
stdin=RarArchiver.devnull,
|
||||||
|
stderr=RarArchiver.devnull)
|
||||||
|
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@ -446,8 +488,8 @@ class RarArchiver:
|
|||||||
namelist.append(item.filename)
|
namelist.append(item.filename)
|
||||||
|
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
print >> sys.stderr, u"getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(
|
print("getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(
|
||||||
str(e), self.path, tries)
|
str(e), self.path, tries), file=sys.stderr)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -464,8 +506,8 @@ class RarArchiver:
|
|||||||
rarc = rarfile.RarFile( self.path )
|
rarc = rarfile.RarFile( self.path )
|
||||||
|
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
print >> sys.stderr, u"getRARObj(): [{0}] {1} attempt#{2}".format(
|
print("getRARObj(): [{0}] {1} attempt#{2}".format(
|
||||||
str(e), self.path, tries)
|
str(e), self.path, tries), file=sys.stderr)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -601,7 +643,7 @@ class ComicArchive:
|
|||||||
logo_data = None
|
logo_data = None
|
||||||
|
|
||||||
class ArchiveType:
|
class ArchiveType:
|
||||||
Zip, Rar, Folder, Pdf, Unknown = range(5)
|
Zip, Rar, Folder, Pdf, Unknown = list(range(5))
|
||||||
|
|
||||||
def __init__(self, path, rar_exe_path=None, default_image_path=None):
|
def __init__(self, path, rar_exe_path=None, default_image_path=None):
|
||||||
self.path = path
|
self.path = path
|
||||||
@ -696,7 +738,7 @@ class ComicArchive:
|
|||||||
if self.archive_type == self.ArchiveType.Unknown:
|
if self.archive_type == self.ArchiveType.Unknown:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
elif check_rar_status and self.isRar() and self.rar_exe_path is None:
|
elif check_rar_status and self.isRar() and not self.rar_exe_path:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
elif not os.access(self.path, os.W_OK):
|
elif not os.access(self.path, os.W_OK):
|
||||||
@ -784,7 +826,7 @@ class ComicArchive:
|
|||||||
try:
|
try:
|
||||||
image_data = self.archiver.readArchiveFile(filename)
|
image_data = self.archiver.readArchiveFile(filename)
|
||||||
except IOError:
|
except IOError:
|
||||||
print >> sys.stderr, u"Error reading in page. Substituting logo page."
|
print("Error reading in page. Substituting logo page.", file=sys.stderr)
|
||||||
image_data = ComicArchive.logo_data
|
image_data = ComicArchive.logo_data
|
||||||
|
|
||||||
return image_data
|
return image_data
|
||||||
@ -826,7 +868,7 @@ class ComicArchive:
|
|||||||
|
|
||||||
# sort by most common
|
# sort by most common
|
||||||
sorted_buckets = sorted(
|
sorted_buckets = sorted(
|
||||||
length_buckets.iteritems(),
|
iter(length_buckets.items()),
|
||||||
key=lambda k_v: (
|
key=lambda k_v: (
|
||||||
k_v[1],
|
k_v[1],
|
||||||
k_v[0]),
|
k_v[0]),
|
||||||
@ -973,7 +1015,7 @@ class ComicArchive:
|
|||||||
try:
|
try:
|
||||||
raw_cix = self.archiver.readArchiveFile(self.ci_xml_filename)
|
raw_cix = self.archiver.readArchiveFile(self.ci_xml_filename)
|
||||||
except IOError:
|
except IOError:
|
||||||
print "Error reading in raw CIX!"
|
print("Error reading in raw CIX!")
|
||||||
raw_cix = ""
|
raw_cix = ""
|
||||||
return raw_cix
|
return raw_cix
|
||||||
|
|
||||||
@ -1042,13 +1084,13 @@ class ComicArchive:
|
|||||||
|
|
||||||
def readRawCoMet(self):
|
def readRawCoMet(self):
|
||||||
if not self.hasCoMet():
|
if not self.hasCoMet():
|
||||||
print >> sys.stderr, self.path, "doesn't have CoMet data!"
|
print(self.path, "doesn't have CoMet data!", file=sys.stderr)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raw_comet = self.archiver.readArchiveFile(self.comet_filename)
|
raw_comet = self.archiver.readArchiveFile(self.comet_filename)
|
||||||
except IOError:
|
except IOError:
|
||||||
print >> sys.stderr, u"Error reading in raw CoMet!"
|
print("Error reading in raw CoMet!", file=sys.stderr)
|
||||||
raw_comet = ""
|
raw_comet = ""
|
||||||
return raw_comet
|
return raw_comet
|
||||||
|
|
||||||
@ -1103,7 +1145,7 @@ class ComicArchive:
|
|||||||
data = self.archiver.readArchiveFile(n)
|
data = self.archiver.readArchiveFile(n)
|
||||||
except:
|
except:
|
||||||
data = ""
|
data = ""
|
||||||
print >> sys.stderr, u"Error reading in Comet XML for validation!"
|
print("Error reading in Comet XML for validation!", file=sys.stderr)
|
||||||
if CoMet().validateString(data):
|
if CoMet().validateString(data):
|
||||||
# since we found it, save it!
|
# since we found it, save it!
|
||||||
self.comet_filename = n
|
self.comet_filename = n
|
||||||
@ -1123,7 +1165,7 @@ class ComicArchive:
|
|||||||
data = self.getPage(idx)
|
data = self.getPage(idx)
|
||||||
if data is not None:
|
if data is not None:
|
||||||
try:
|
try:
|
||||||
im = Image.open(StringIO.StringIO(data))
|
im = Image.open(io.StringIO(data))
|
||||||
w, h = im.size
|
w, h = im.size
|
||||||
|
|
||||||
p['ImageSize'] = str(len(data))
|
p['ImageSize'] = str(len(data))
|
||||||
|
@ -18,8 +18,8 @@ import json
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
#import zipfile
|
#import zipfile
|
||||||
|
|
||||||
from genericmetadata import GenericMetadata
|
from .genericmetadata import GenericMetadata
|
||||||
import utils
|
from . import utils
|
||||||
#import ctversion
|
#import ctversion
|
||||||
|
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class ComicBookInfo:
|
|||||||
|
|
||||||
def metadataFromString(self, string):
|
def metadataFromString(self, string):
|
||||||
|
|
||||||
cbi_container = json.loads(unicode(string, 'utf-8'))
|
cbi_container = json.loads(str(string, 'utf-8'))
|
||||||
|
|
||||||
metadata = GenericMetadata()
|
metadata = GenericMetadata()
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ class ComicBookInfo:
|
|||||||
# helper func
|
# helper func
|
||||||
def toInt(s):
|
def toInt(s):
|
||||||
i = None
|
i = None
|
||||||
if type(s) in [str, unicode, int]:
|
if type(s) in [str, str, int]:
|
||||||
try:
|
try:
|
||||||
i = int(s)
|
i = int(s)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -19,8 +19,8 @@ import xml.etree.ElementTree as ET
|
|||||||
#from pprint import pprint
|
#from pprint import pprint
|
||||||
#import zipfile
|
#import zipfile
|
||||||
|
|
||||||
from genericmetadata import GenericMetadata
|
from .genericmetadata import GenericMetadata
|
||||||
import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
class ComicInfoXml:
|
class ComicInfoXml:
|
||||||
@ -54,7 +54,8 @@ class ComicInfoXml:
|
|||||||
header = '<?xml version="1.0"?>\n'
|
header = '<?xml version="1.0"?>\n'
|
||||||
|
|
||||||
tree = self.convertMetadataToXML(self, metadata)
|
tree = self.convertMetadataToXML(self, metadata)
|
||||||
return header + ET.tostring(tree.getroot())
|
tree_str = ET.tostring(tree.getroot()).decode()
|
||||||
|
return header + tree_str
|
||||||
|
|
||||||
def indent(self, elem, level=0):
|
def indent(self, elem, level=0):
|
||||||
# for making the XML output readable
|
# for making the XML output readable
|
||||||
@ -85,7 +86,7 @@ class ComicInfoXml:
|
|||||||
|
|
||||||
def assign(cix_entry, md_entry):
|
def assign(cix_entry, md_entry):
|
||||||
if md_entry is not None:
|
if md_entry is not None:
|
||||||
ET.SubElement(root, cix_entry).text = u"{0}".format(md_entry)
|
ET.SubElement(root, cix_entry).text = "{0}".format(md_entry)
|
||||||
|
|
||||||
assign('Title', md.title)
|
assign('Title', md.title)
|
||||||
assign('Series', md.series)
|
assign('Series', md.series)
|
||||||
|
@ -22,7 +22,7 @@ This should probably be re-written, but, well, it mostly works!
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
from urllib import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
|
|
||||||
class FileNameParser:
|
class FileNameParser:
|
||||||
|
@ -20,7 +20,7 @@ possible, however lossy it might be
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
class PageType:
|
class PageType:
|
||||||
@ -251,7 +251,7 @@ class GenericMetadata:
|
|||||||
return "No metadata"
|
return "No metadata"
|
||||||
|
|
||||||
def add_string(tag, val):
|
def add_string(tag, val):
|
||||||
if val is not None and u"{0}".format(val) != "":
|
if val is not None and "{0}".format(val) != "":
|
||||||
vals.append((tag, val))
|
vals.append((tag, val))
|
||||||
|
|
||||||
def add_attr_string(tag):
|
def add_attr_string(tag):
|
||||||
@ -314,7 +314,7 @@ class GenericMetadata:
|
|||||||
|
|
||||||
# format the data nicely
|
# format the data nicely
|
||||||
outstr = ""
|
outstr = ""
|
||||||
fmt_str = u"{0: <" + str(flen) + "} {1}\n"
|
fmt_str = "{0: <" + str(flen) + "} {1}\n"
|
||||||
for i in vals:
|
for i in vals:
|
||||||
outstr += fmt_str.format(i[0] + ":", i[1])
|
outstr += fmt_str.format(i[0] + ":", i[1])
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class IssueString:
|
|||||||
if len(text) == 0:
|
if len(text) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
text = unicode(text)
|
text = str(text)
|
||||||
|
|
||||||
# skip the minus sign if it's first
|
# skip the minus sign if it's first
|
||||||
if text[0] == '-':
|
if text[0] == '-':
|
||||||
@ -119,7 +119,7 @@ class IssueString:
|
|||||||
|
|
||||||
def asFloat(self):
|
def asFloat(self):
|
||||||
# return the float, with no suffix
|
# return the float, with no suffix
|
||||||
if self.suffix == u"½":
|
if self.suffix == "½":
|
||||||
if self.num is not None:
|
if self.num is not None:
|
||||||
return self.num + .5
|
return self.num + .5
|
||||||
else:
|
else:
|
||||||
|
@ -55,20 +55,22 @@ def get_recursive_filelist(pathlist):
|
|||||||
# if path is a folder, walk it recursively, and all files underneath
|
# if path is a folder, walk it recursively, and all files underneath
|
||||||
if isinstance(p, str):
|
if isinstance(p, str):
|
||||||
# make sure string is unicode
|
# make sure string is unicode
|
||||||
p = p.decode(filename_encoding) # , 'replace')
|
#p = p.decode(filename_encoding) # , 'replace')
|
||||||
elif not isinstance(p, unicode):
|
pass
|
||||||
|
elif not isinstance(p, str):
|
||||||
# it's probably a QString
|
# it's probably a QString
|
||||||
p = unicode(p)
|
p = str(p)
|
||||||
|
|
||||||
if os.path.isdir(p):
|
if os.path.isdir(p):
|
||||||
for root, dirs, files in os.walk(p):
|
for root, dirs, files in os.walk(p):
|
||||||
for f in files:
|
for f in files:
|
||||||
if isinstance(f, str):
|
if isinstance(f, str):
|
||||||
# make sure string is unicode
|
# make sure string is unicode
|
||||||
f = f.decode(filename_encoding, 'replace')
|
#f = f.decode(filename_encoding, 'replace')
|
||||||
elif not isinstance(f, unicode):
|
pass
|
||||||
|
elif not isinstance(f, str):
|
||||||
# it's probably a QString
|
# it's probably a QString
|
||||||
f = unicode(f)
|
f = str(f)
|
||||||
filelist.append(os.path.join(root, f))
|
filelist.append(os.path.join(root, f))
|
||||||
else:
|
else:
|
||||||
filelist.append(p)
|
filelist.append(p)
|
||||||
@ -121,7 +123,7 @@ def which(program):
|
|||||||
|
|
||||||
def removearticles(text):
|
def removearticles(text):
|
||||||
text = text.lower()
|
text = text.lower()
|
||||||
articles = ['and', 'the', 'a', '&', 'issue']
|
articles = ['and', 'a', '&', 'issue', 'the']
|
||||||
newText = ''
|
newText = ''
|
||||||
for word in text.split(' '):
|
for word in text.split(' '):
|
||||||
if word not in articles:
|
if word not in articles:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user