iogp 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iogp-1.0.1/.gitignore +8 -0
- iogp-1.0.1/LICENSE +21 -0
- iogp-1.0.1/PKG-INFO +91 -0
- iogp-1.0.1/README.md +57 -0
- iogp-1.0.1/external/libarchive.license +26 -0
- iogp-1.0.1/iogp/__init__.py +6 -0
- iogp-1.0.1/iogp/data/__init__.py +10 -0
- iogp-1.0.1/iogp/data/archive.py +260 -0
- iogp-1.0.1/iogp/data/carve.py +209 -0
- iogp-1.0.1/iogp/data/dataview.py +214 -0
- iogp-1.0.1/iogp/data/ds.py +114 -0
- iogp-1.0.1/iogp/data/fileid.py +282 -0
- iogp-1.0.1/iogp/data/str.py +13 -0
- iogp-1.0.1/iogp/db/__init__.py +10 -0
- iogp-1.0.1/iogp/db/cache.py +356 -0
- iogp-1.0.1/iogp/db/dbobj.py +247 -0
- iogp-1.0.1/iogp/db/sqlite.py +253 -0
- iogp-1.0.1/iogp/gui/__init__.py +12 -0
- iogp-1.0.1/iogp/gui/qt.py +148 -0
- iogp-1.0.1/iogp/gui/qtwidgets.py +19 -0
- iogp-1.0.1/iogp/net/__init__.py +7 -0
- iogp-1.0.1/iogp/net/net.py +229 -0
- iogp-1.0.1/iogp/net/packet.py +267 -0
- iogp-1.0.1/iogp/win/__init__.py +10 -0
- iogp-1.0.1/iogp/win/win.py +227 -0
- iogp-1.0.1/pyproject.toml +34 -0
- iogp-1.0.1/tests/test.py +70 -0
- iogp-1.0.1/tests/test.zip +0 -0
iogp-1.0.1/.gitignore
ADDED
iogp-1.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018-2023 Vlad Ioan Topan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
iogp-1.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: iogp
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Python common task, general purpose library (serialization, networking, etc.).
|
|
5
|
+
Project-URL: Homepage, https://gitlab.com/vtopan/iogp
|
|
6
|
+
Author-email: Vlad Topan <vtopan@gmail.removethispart.com>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2018-2023 Vlad Ioan Topan
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
30
|
+
Classifier: Programming Language :: Python
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Requires-Python: >=3.9
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# iogp
|
|
36
|
+
|
|
37
|
+
Python common task, general purpose library (serialization, networking, etc.).
|
|
38
|
+
Note: this project used to be called iolib - it was renamed for PyPI.
|
|
39
|
+
|
|
40
|
+
## iogp.net
|
|
41
|
+
|
|
42
|
+
- provides the `download()` function
|
|
43
|
+
- automatic caching of downloads
|
|
44
|
+
- supports cookies, authentication, etc.
|
|
45
|
+
- can spoof user agents (or the entire GET request) - see the `USER_AGENTS` dict in `net/net.py`
|
|
46
|
+
- examples:
|
|
47
|
+
- `text = iogp.net.download('https://google.com')`
|
|
48
|
+
- `info = {}; iogp.net.download('https://1.1.1.1', info=info, cache=0)` => `info['code'] == 200`,
|
|
49
|
+
`info['headers']['Content-Type']`, etc.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## iogp.db
|
|
53
|
+
|
|
54
|
+
- provides a persistence / caching / storage object `DataCache` that can store e.g. the results from a downloaded URL
|
|
55
|
+
- use `DataStore` to also get in-memory caching of values
|
|
56
|
+
- `ObjCache()` wraps `DataCache` to generate the storage filename automatically
|
|
57
|
+
- usage:
|
|
58
|
+
|
|
59
|
+
~~~Python
|
|
60
|
+
from iogp.db import DataCache
|
|
61
|
+
|
|
62
|
+
fn = 'objects.db'
|
|
63
|
+
|
|
64
|
+
db = DataCache(fn)
|
|
65
|
+
db['key1'] = {'some':['serializable', 'object', 0]}
|
|
66
|
+
db['some-other-key'] = 0
|
|
67
|
+
db.close()
|
|
68
|
+
|
|
69
|
+
db = DataCache(fn)
|
|
70
|
+
print(db['key1'])
|
|
71
|
+
print('key2' in db, 'some-other-key' in db)
|
|
72
|
+
~~~
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## iogp.win
|
|
76
|
+
|
|
77
|
+
- ctypes wrapper over some Windows-specific functions
|
|
78
|
+
- example - iterating all windows and retrieving the window title for each one:
|
|
79
|
+
|
|
80
|
+
~~~Python
|
|
81
|
+
import iogp.win as win
|
|
82
|
+
|
|
83
|
+
def callback(hwnd, lparam):
|
|
84
|
+
size = 512
|
|
85
|
+
buf = win.unicode_buffer(size)
|
|
86
|
+
win.GetWindowText(hwnd, buf, size)
|
|
87
|
+
print(f'[-] Handle: {hwnd}, title: {buf.value}')
|
|
88
|
+
return 1 # 0 to stop
|
|
89
|
+
|
|
90
|
+
win.EnumWindows(win.WNDENUMPROC(callback), 0)
|
|
91
|
+
~~~
|
iogp-1.0.1/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# iogp
|
|
2
|
+
|
|
3
|
+
Python common task, general purpose library (serialization, networking, etc.).
|
|
4
|
+
Note: this project used to be called iolib - it was renamed for PyPI.
|
|
5
|
+
|
|
6
|
+
## iogp.net
|
|
7
|
+
|
|
8
|
+
- provides the `download()` function
|
|
9
|
+
- automatic caching of downloads
|
|
10
|
+
- supports cookies, authentication, etc.
|
|
11
|
+
- can spoof user agents (or the entire GET request) - see the `USER_AGENTS` dict in `net/net.py`
|
|
12
|
+
- examples:
|
|
13
|
+
- `text = iogp.net.download('https://google.com')`
|
|
14
|
+
- `info = {}; iogp.net.download('https://1.1.1.1', info=info, cache=0)` => `info['code'] == 200`,
|
|
15
|
+
`info['headers']['Content-Type']`, etc.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## iogp.db
|
|
19
|
+
|
|
20
|
+
- provides a persistence / caching / storage object `DataCache` that can store e.g. the results from a downloaded URL
|
|
21
|
+
- use `DataStore` to also get in-memory caching of values
|
|
22
|
+
- `ObjCache()` wraps `DataCache` to generate the storage filename automatically
|
|
23
|
+
- usage:
|
|
24
|
+
|
|
25
|
+
~~~Python
|
|
26
|
+
from iogp.db import DataCache
|
|
27
|
+
|
|
28
|
+
fn = 'objects.db'
|
|
29
|
+
|
|
30
|
+
db = DataCache(fn)
|
|
31
|
+
db['key1'] = {'some':['serializable', 'object', 0]}
|
|
32
|
+
db['some-other-key'] = 0
|
|
33
|
+
db.close()
|
|
34
|
+
|
|
35
|
+
db = DataCache(fn)
|
|
36
|
+
print(db['key1'])
|
|
37
|
+
print('key2' in db, 'some-other-key' in db)
|
|
38
|
+
~~~
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## iogp.win
|
|
42
|
+
|
|
43
|
+
- ctypes wrapper over some Windows-specific functions
|
|
44
|
+
- example - iterating all windows and retrieving the window title for each one:
|
|
45
|
+
|
|
46
|
+
~~~Python
|
|
47
|
+
import iogp.win as win
|
|
48
|
+
|
|
49
|
+
def callback(hwnd, lparam):
|
|
50
|
+
size = 512
|
|
51
|
+
buf = win.unicode_buffer(size)
|
|
52
|
+
win.GetWindowText(hwnd, buf, size)
|
|
53
|
+
print(f'[-] Handle: {hwnd}, title: {buf.value}')
|
|
54
|
+
return 1 # 0 to stop
|
|
55
|
+
|
|
56
|
+
win.EnumWindows(win.WNDENUMPROC(callback), 0)
|
|
57
|
+
~~~
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*-
|
|
2
|
+
* Copyright (c) 2003-2010 Tim Kientzle
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted provided that the following conditions
|
|
7
|
+
* are met:
|
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
|
13
|
+
*
|
|
14
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
|
15
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
16
|
+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
17
|
+
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
18
|
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
19
|
+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
20
|
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
21
|
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
22
|
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
23
|
+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+
*
|
|
25
|
+
* $FreeBSD: src/lib/libarchive/archive.h.in,v 1.50 2008/05/26 17:00:22 kientzle Exp $
|
|
26
|
+
*/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
iogp.data: Various data processing tools.
|
|
3
|
+
|
|
4
|
+
Author: Vlad Topan (vtopan/gmail)
|
|
5
|
+
"""
|
|
6
|
+
from .fileid import get_file_type, get_ft_category, get_ft_ext, FT_UNKNOWN, FT_EMPTY, FT_CATEGORY
|
|
7
|
+
from .carve import extract_embedded_files
|
|
8
|
+
from .ds import AttrDict, Config, dict_to_AttrDict
|
|
9
|
+
from .archive import Archive
|
|
10
|
+
from .str import ellipsis
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""
|
|
2
|
+
iogp.data.archive: Archive processing APIs.
|
|
3
|
+
|
|
4
|
+
On Windows, archiveint.dll (libarchive 3.3.2?) is used (ships with Windows 10).
|
|
5
|
+
Code from libarchive's archive.h is used, license in external/libarchive.license.
|
|
6
|
+
|
|
7
|
+
Author: Vlad Topan (vtopan/gmail)
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import ctypes
|
|
11
|
+
from ctypes import c_void_p, Structure, c_int, c_char_p, POINTER, byref
|
|
12
|
+
if os.name == 'nt':
|
|
13
|
+
from ctypes import WINFUNCTYPE, windll
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
ARCHIVE_FORMAT_BASE_MASK = 0xff0000
|
|
17
|
+
ARCHIVE_FORMAT_CPIO = 0x10000
|
|
18
|
+
ARCHIVE_FORMAT_CPIO_POSIX = (ARCHIVE_FORMAT_CPIO | 1)
|
|
19
|
+
ARCHIVE_FORMAT_CPIO_BIN_LE = (ARCHIVE_FORMAT_CPIO | 2)
|
|
20
|
+
ARCHIVE_FORMAT_CPIO_BIN_BE = (ARCHIVE_FORMAT_CPIO | 3)
|
|
21
|
+
ARCHIVE_FORMAT_CPIO_SVR4_NOCRC = (ARCHIVE_FORMAT_CPIO | 4)
|
|
22
|
+
ARCHIVE_FORMAT_CPIO_SVR4_CRC = (ARCHIVE_FORMAT_CPIO | 5)
|
|
23
|
+
ARCHIVE_FORMAT_CPIO_AFIO_LARGE = (ARCHIVE_FORMAT_CPIO | 6)
|
|
24
|
+
ARCHIVE_FORMAT_SHAR = 0x20000
|
|
25
|
+
ARCHIVE_FORMAT_SHAR_BASE = (ARCHIVE_FORMAT_SHAR | 1)
|
|
26
|
+
ARCHIVE_FORMAT_SHAR_DUMP = (ARCHIVE_FORMAT_SHAR | 2)
|
|
27
|
+
ARCHIVE_FORMAT_TAR = 0x30000
|
|
28
|
+
ARCHIVE_FORMAT_TAR_USTAR = (ARCHIVE_FORMAT_TAR | 1)
|
|
29
|
+
ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE = (ARCHIVE_FORMAT_TAR | 2)
|
|
30
|
+
ARCHIVE_FORMAT_TAR_PAX_RESTRICTED = (ARCHIVE_FORMAT_TAR | 3)
|
|
31
|
+
ARCHIVE_FORMAT_TAR_GNUTAR = (ARCHIVE_FORMAT_TAR | 4)
|
|
32
|
+
ARCHIVE_FORMAT_ISO9660 = 0x40000
|
|
33
|
+
ARCHIVE_FORMAT_ISO9660_ROCKRIDGE = (ARCHIVE_FORMAT_ISO9660 | 1)
|
|
34
|
+
ARCHIVE_FORMAT_ZIP = 0x50000
|
|
35
|
+
ARCHIVE_FORMAT_EMPTY = 0x60000
|
|
36
|
+
ARCHIVE_FORMAT_AR = 0x70000
|
|
37
|
+
ARCHIVE_FORMAT_AR_GNU = (ARCHIVE_FORMAT_AR | 1)
|
|
38
|
+
ARCHIVE_FORMAT_AR_BSD = (ARCHIVE_FORMAT_AR | 2)
|
|
39
|
+
ARCHIVE_FORMAT_MTREE = 0x80000
|
|
40
|
+
ARCHIVE_FORMAT_RAW = 0x90000
|
|
41
|
+
ARCHIVE_FORMAT_XAR = 0xA0000
|
|
42
|
+
ARCHIVE_FORMAT_LHA = 0xB0000
|
|
43
|
+
ARCHIVE_FORMAT_CAB = 0xC0000
|
|
44
|
+
ARCHIVE_FORMAT_RAR = 0xD0000
|
|
45
|
+
ARCHIVE_FORMAT_7ZIP = 0xE0000
|
|
46
|
+
ARCHIVE_FORMAT_WARC = 0xF0000
|
|
47
|
+
ARCHIVE_FORMAT_RAR_V5 = 0x100000
|
|
48
|
+
|
|
49
|
+
ARCHIVE_EOF = 1 # Found end of archive.
|
|
50
|
+
ARCHIVE_OK = 0 # Operation was successful.
|
|
51
|
+
ARCHIVE_RETRY = (-10) # Retry might succeed.
|
|
52
|
+
ARCHIVE_WARN = (-20) # Partial success.
|
|
53
|
+
ARCHIVE_FAILED = (-25) # Current operation cannot complete.
|
|
54
|
+
ARCHIVE_FATAL = (-30) # No more operations are possible.
|
|
55
|
+
|
|
56
|
+
MODE_READ = 0
|
|
57
|
+
MODE_WRITE = 1
|
|
58
|
+
|
|
59
|
+
DEF_BUFFER_SIZE = 16384
|
|
60
|
+
|
|
61
|
+
struct_archive = c_void_p
|
|
62
|
+
struct_archive_entry = c_void_p
|
|
63
|
+
|
|
64
|
+
if os.name == 'nt':
|
|
65
|
+
ARCHIVEINT_LIB = windll.archiveint
|
|
66
|
+
# todo: load libarchive.so on POSIX if available instead of crashing
|
|
67
|
+
|
|
68
|
+
get_zlib_version = WINFUNCTYPE(c_char_p)(('archive_zlib_version', ARCHIVEINT_LIB))
|
|
69
|
+
get_zlib_version.errcheck = lambda res, _1, _2: res.decode('utf8')
|
|
70
|
+
|
|
71
|
+
archive_read_new = WINFUNCTYPE(struct_archive)(('archive_read_new', ARCHIVEINT_LIB))
|
|
72
|
+
archive_read_support_filter_all = WINFUNCTYPE(c_int, struct_archive)(('archive_read_support_filter_all', ARCHIVEINT_LIB))
|
|
73
|
+
archive_read_support_format_all = WINFUNCTYPE(c_int, struct_archive)(('archive_read_support_format_all', ARCHIVEINT_LIB))
|
|
74
|
+
archive_read_support_format_raw = WINFUNCTYPE(c_int, struct_archive)(('archive_read_support_format_raw', ARCHIVEINT_LIB))
|
|
75
|
+
archive_read_support_compression_all = WINFUNCTYPE(c_int, struct_archive)(('archive_read_support_compression_all', ARCHIVEINT_LIB))
|
|
76
|
+
archive_read_open_filename = WINFUNCTYPE(c_int, struct_archive, c_char_p, c_int)(('archive_read_open_filename', ARCHIVEINT_LIB))
|
|
77
|
+
archive_read_open_memory = WINFUNCTYPE(c_int, struct_archive, c_char_p, c_int)(('archive_read_open_memory', ARCHIVEINT_LIB))
|
|
78
|
+
archive_read_next_header = WINFUNCTYPE(c_int, struct_archive, POINTER(struct_archive_entry))(('archive_read_next_header', ARCHIVEINT_LIB))
|
|
79
|
+
archive_entry_pathname = WINFUNCTYPE(c_char_p, struct_archive_entry)(('archive_entry_pathname', ARCHIVEINT_LIB))
|
|
80
|
+
archive_entry_pathname.errcheck = lambda res, _1, _2: res.decode('utf8') if res else ''
|
|
81
|
+
archive_read_data = WINFUNCTYPE(c_int, struct_archive, c_char_p, c_int)(('archive_read_data', ARCHIVEINT_LIB))
|
|
82
|
+
archive_read_data_skip = WINFUNCTYPE(c_int, struct_archive)(('archive_read_data_skip', ARCHIVEINT_LIB))
|
|
83
|
+
archive_read_data_block = WINFUNCTYPE(c_int, struct_archive, POINTER(c_char_p), POINTER(c_int), POINTER(c_int))(('archive_read_data_block', ARCHIVEINT_LIB))
|
|
84
|
+
archive_read_free = WINFUNCTYPE(c_int, struct_archive)(('archive_read_free', ARCHIVEINT_LIB))
|
|
85
|
+
archive_read_close = WINFUNCTYPE(c_int, struct_archive)(('archive_read_close', ARCHIVEINT_LIB))
|
|
86
|
+
archive_compression_name = WINFUNCTYPE(c_char_p, struct_archive_entry)(('archive_compression_name', ARCHIVEINT_LIB))
|
|
87
|
+
archive_compression_name.errcheck = lambda res, _1, _2: res.decode('utf8') if res else ''
|
|
88
|
+
archive_format_name = WINFUNCTYPE(c_char_p, struct_archive_entry)(('archive_format_name', ARCHIVEINT_LIB))
|
|
89
|
+
archive_format_name.errcheck = lambda res, _1, _2: res.decode('utf8') if res else ''
|
|
90
|
+
archive_error_string = WINFUNCTYPE(c_char_p)(('archive_error_string', ARCHIVEINT_LIB))
|
|
91
|
+
archive_error_string.errcheck = lambda res, _1, _2: res.decode('utf8') if res else ''
|
|
92
|
+
else:
|
|
93
|
+
# todo: find alternatives on Linux
|
|
94
|
+
archive_read_new = None
|
|
95
|
+
archive_read_support_filter_all = None
|
|
96
|
+
archive_read_support_format_all = None
|
|
97
|
+
archive_read_support_compression_all = None
|
|
98
|
+
archive_read_open_filename = None
|
|
99
|
+
archive_read_open_memory = None
|
|
100
|
+
archive_read_free = None
|
|
101
|
+
archive_read_next_header = None
|
|
102
|
+
archive_entry_pathname = None
|
|
103
|
+
archive_read_data_block = None
|
|
104
|
+
archive_compression_name = None
|
|
105
|
+
archive_format_name = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ArchiveError(ValueError): pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class Archive:
|
|
113
|
+
"""
|
|
114
|
+
Archive object.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(self, filename_or_data=None):
|
|
118
|
+
self.mode = MODE_READ
|
|
119
|
+
self.archive = None
|
|
120
|
+
if filename_or_data:
|
|
121
|
+
self.open_archive(filename_or_data)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def last_error(self):
|
|
125
|
+
"""
|
|
126
|
+
Returns the last error from libarchive.
|
|
127
|
+
"""
|
|
128
|
+
return archive_error_string()
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def open_archive(self, filename_or_data, buffer_size=DEF_BUFFER_SIZE):
|
|
132
|
+
"""
|
|
133
|
+
Open an archive file or buffer for reading.
|
|
134
|
+
"""
|
|
135
|
+
self.mode = MODE_READ
|
|
136
|
+
self.buffer_size = buffer_size
|
|
137
|
+
self.source = filename_or_data
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def reset(self):
|
|
141
|
+
"""
|
|
142
|
+
Reset (reopen) the archive.
|
|
143
|
+
"""
|
|
144
|
+
self.close()
|
|
145
|
+
self.archive = arc = archive_read_new()
|
|
146
|
+
if archive_read_support_filter_all(arc) != ARCHIVE_OK:
|
|
147
|
+
raise ArchiveError(f'Failed enabling filters: {self.last_error()})!')
|
|
148
|
+
if archive_read_support_format_all(arc) != ARCHIVE_OK:
|
|
149
|
+
raise ArchiveError(f'Failed enabling formats: {self.last_error()})!')
|
|
150
|
+
#if archive_read_support_compression_all(arc) != ARCHIVE_OK:
|
|
151
|
+
# raise ArchiveError(f'Failed enabling compression: {self.last_error()})!')
|
|
152
|
+
if type(self.source) is str:
|
|
153
|
+
res = archive_read_open_filename(arc, self.source.encode('utf8'), self.buffer_size)
|
|
154
|
+
else:
|
|
155
|
+
res = archive_read_open_memory(arc, self.source, self.buffer_size)
|
|
156
|
+
if res != ARCHIVE_OK:
|
|
157
|
+
raise ArchiveError(f'Failed opening file or buffer: {res} ({self.last_error()})!')
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def close(self):
|
|
161
|
+
"""
|
|
162
|
+
Close the archive.
|
|
163
|
+
"""
|
|
164
|
+
if self.archive:
|
|
165
|
+
archive_read_free(self.archive)
|
|
166
|
+
self.archive = None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def walk(self, callback=None):
|
|
170
|
+
"""
|
|
171
|
+
Iterate archive member filenames returning tuples (name, entry_object).
|
|
172
|
+
"""
|
|
173
|
+
if (not self.mode == MODE_READ) and self.source:
|
|
174
|
+
raise ArchiveError('No archive is opened for reading!')
|
|
175
|
+
self.reset()
|
|
176
|
+
entry = struct_archive_entry()
|
|
177
|
+
while archive_read_next_header(self.archive, ctypes.byref(entry)) == ARCHIVE_OK:
|
|
178
|
+
entry_name = archive_entry_pathname(entry)
|
|
179
|
+
if callback:
|
|
180
|
+
callback(entry_name, entry)
|
|
181
|
+
yield entry_name, entry
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def list_members(self):
|
|
186
|
+
"""
|
|
187
|
+
Iterate over member names.
|
|
188
|
+
"""
|
|
189
|
+
for name, e in self.walk():
|
|
190
|
+
yield name
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def read(self, filename=None):
|
|
194
|
+
"""
|
|
195
|
+
Read an archive member (the current one if none given); returns None if the filename is given and not present.
|
|
196
|
+
|
|
197
|
+
Do NOT use this for large files - the data will be read to memory.
|
|
198
|
+
"""
|
|
199
|
+
if not filename:
|
|
200
|
+
return self.read_crt_entry()
|
|
201
|
+
for name, e in self.walk():
|
|
202
|
+
if name == filename:
|
|
203
|
+
return self.read_crt_entry()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def read_crt_entry(self):
|
|
207
|
+
"""
|
|
208
|
+
Read the current archive entry.
|
|
209
|
+
"""
|
|
210
|
+
if (not self.mode == MODE_READ) and self.archive:
|
|
211
|
+
raise ArchiveError('No archive is opened for reading!')
|
|
212
|
+
chunk = c_char_p()
|
|
213
|
+
chunk_size = c_int()
|
|
214
|
+
offset = c_int(0)
|
|
215
|
+
lst = []
|
|
216
|
+
while 1:
|
|
217
|
+
res = archive_read_data_block(self.archive, byref(chunk), byref(chunk_size), byref(offset))
|
|
218
|
+
offset.value += chunk_size.value
|
|
219
|
+
if res == ARCHIVE_EOF:
|
|
220
|
+
break
|
|
221
|
+
elif res != ARCHIVE_OK:
|
|
222
|
+
raise ArchiveError(f'Failed reading current archive entry: {res} ({self.last_error()})!')
|
|
223
|
+
lst.append(chunk.value[:chunk_size.value])
|
|
224
|
+
return b''.join(lst)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def get_format(self):
|
|
228
|
+
"""
|
|
229
|
+
Returns the format of the current archive.
|
|
230
|
+
"""
|
|
231
|
+
if (not self.mode == MODE_READ) and self.source:
|
|
232
|
+
raise ArchiveError('No archive is opened for reading!')
|
|
233
|
+
if not self.archive:
|
|
234
|
+
self.reset()
|
|
235
|
+
return archive_compression_name(self.archive)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def get_entry_format(self):
|
|
239
|
+
"""
|
|
240
|
+
Returns the format of the current archive entry.
|
|
241
|
+
"""
|
|
242
|
+
if (not self.mode == MODE_READ) and self.archive:
|
|
243
|
+
raise ArchiveError('No archive is opened for reading!')
|
|
244
|
+
return archive_format_name(self.archive)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
"""
|
|
249
|
+
Example:
|
|
250
|
+
|
|
251
|
+
a = Archive('test.zip')
|
|
252
|
+
print(a.get_format())
|
|
253
|
+
for name in a.list_members():
|
|
254
|
+
print(name, a.get_entry_format())
|
|
255
|
+
if name == 'extractme.ext':
|
|
256
|
+
data = a.read()
|
|
257
|
+
open(name, 'wb').write(data)
|
|
258
|
+
print(a.read('test.py'))
|
|
259
|
+
a.close()
|
|
260
|
+
"""
|