Skip to content

Commit 2bd24b8

Browse files
committed
Try harder to keep python 2 compat
Fleshed out the cli command, which only supports listing content at the moment Add the signxml dependency for verifying xar signatures.
1 parent c906d37 commit 2bd24b8

File tree

7 files changed

+101
-18
lines changed

7 files changed

+101
-18
lines changed

bixar/archive.py

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
from __future__ import print_function
2+
from typing import List, Union, Generator, Tuple
13
import os
24
import io
3-
from typing import List, Union, Generator, Tuple
45
import datetime
56
import zlib
67
import gzip
78
import hashlib
89
from struct import *
910
from collections import namedtuple
10-
from collections.abc import Container
1111
import xml.etree.ElementTree as ET
1212

1313
from bixar.errors import XarError, XarChecksumError
@@ -16,7 +16,7 @@
1616
class XarInfo(object):
1717
"""Just like TarInfo, a XarInfo represents one member in a XarFile. It does not contain the file data."""
1818

19-
def __init__(self, name = ''):
19+
def __init__(self, name=''):
2020
self._name = name
2121

2222
@classmethod
@@ -65,7 +65,7 @@ def gid(self) -> Union[int, None]:
6565
return int(self._element.find('gid').text)
6666
else:
6767
return None
68-
68+
6969
@property
7070
def uname(self) -> Union[str, None]:
7171
user = self._element.find('user')
@@ -102,7 +102,6 @@ def data_archived_checksum(self) -> Tuple[str, str]:
102102

103103
return alg, value
104104

105-
106105
def heap_location(self) -> Tuple[int, int]:
107106
"""Get the heap location as length, offset"""
108107
data = self._element.find('data')
@@ -117,6 +116,7 @@ def isfile(self) -> bool:
117116
def isdir(self) -> bool:
118117
return self._element.find('type').text == 'directory'
119118

119+
120120
#
121121
# class XarSignature(object):
122122
#
@@ -134,10 +134,10 @@ class XarExtendedSignature(object):
134134
pass
135135

136136

137-
138137
XarHeader = namedtuple('XarHeader', 'magic size version toc_len_compressed toc_len_uncompressed cksumalg')
139138

140-
class XarFile(Container):
139+
140+
class XarFile(object):
141141
"""XAR Archive
142142
143143
This class is written in the style of TarFile
@@ -230,11 +230,22 @@ def extractall(self, path: str = "."):
230230

231231
self._extract_recursive(self._toc.find('toc'), path)
232232

233-
def extract(self, matching: str):
233+
def extract(self, member, path="", set_attrs=True, *, numeric_owner=False):
234+
pass
235+
236+
def extractfile(self, member):
234237
pass
235238

239+
def _getnames(self, element: ET.Element, prefix=''):
240+
for f in element.findall('file'):
241+
yield os.path.join(prefix, f.find('name').text)
242+
243+
if f.findtext('type') == 'directory':
244+
for cf in self._getnames(f, prefix=prefix + f.findtext('name')):
245+
yield cf
246+
236247
def getnames(self) -> List[str]:
237-
return [m.name for m in self.getmembers()]
248+
return [m for m in self._getnames(self._toc.find('toc'))]
238249

239250
def _getmembers(self, element: ET.Element):
240251
for f in element.findall('file'):
@@ -248,13 +259,12 @@ def getmembers(self) -> List[XarInfo]:
248259
infos = []
249260

250261
toc = self._toc.find('toc')
251-
262+
252263
for m in self._getmembers(toc):
253264
infos.append(m)
254265

255266
return infos
256267

257-
258268
@classmethod
259269
def is_xarfile(cls, name: str) -> bool:
260270
"""Return True if *name* is a xar archive file, that the moxar module can read."""
@@ -263,16 +273,17 @@ def is_xarfile(cls, name: str) -> bool:
263273
return magic == b'xar!'
264274

265275
@classmethod
266-
def open(cls, name: str) -> any:
267-
with open(name, 'rb') as fd:
268-
header = fd.read(28) # The spec says the header must be at least 28
276+
def open(cls, name=None, mode='r', fileobj=None) -> any:
277+
if fileobj is None:
278+
fileobj = open(name, mode)
269279

270-
if header[:4] != b'xar!':
271-
raise ValueError('Not a XAR Archive')
280+
header = fileobj.read(28) # The spec says the header must be at least 28
272281

273-
hdr = XarHeader._make(XarFile.Header.unpack(header))
274-
toc_compressed = fd.read(hdr.toc_len_compressed)
282+
if header[:4] != b'xar!':
283+
raise ValueError('Not a XAR Archive')
275284

285+
hdr = XarHeader._make(XarFile.Header.unpack(header))
286+
toc_compressed = fileobj.read(hdr.toc_len_compressed)
276287
toc_uncompressed = zlib.decompress(toc_compressed)
277288

278289
if len(toc_uncompressed) != hdr.toc_len_uncompressed:

bixar/cli.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,43 @@
1+
from __future__ import print_function
12
import argparse
3+
from .archive import XarFile
4+
5+
6+
def main():
7+
parser = argparse.ArgumentParser(description='bixar test XAR implementation')
8+
parser.add_argument('-c', help='Creates an archive', action='store_true')
9+
parser.add_argument('-x', help='Extracts an archive', action='store_true')
10+
parser.add_argument('-t', help='Lists an archive', action='store_true')
11+
parser.add_argument('-f',
12+
help='Specifies an archive to operate on [REQUIRED!]',
13+
required=True, metavar='filename', type=argparse.FileType('rb'))
14+
parser.add_argument('-v', help='Print filenames as they are archived', action='store_true')
15+
16+
args = parser.parse_args()
17+
18+
if args.t:
19+
return list_archive(args)
20+
elif args.c:
21+
print('command not implemented')
22+
return 1
23+
elif args.x:
24+
print('command not implemented')
25+
return 1
26+
27+
return 0
28+
29+
30+
def create():
31+
pass
32+
33+
34+
def extract():
35+
pass
36+
37+
38+
def list_archive(args):
39+
archive = XarFile.open(fileobj=args.f)
40+
41+
for f in archive.getnames():
42+
print(f)
43+

bixar/errors.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
class XarError(Exception):
2+
"""Catch-all error"""
23
pass
34

45

56
class XarChecksumError(Exception):
67
"""Raised when you attempt to extract a file with an invalid checksum."""
78
pass
9+
10+
11+
class XarSigningError(Exception):
12+
"""Raised when the xar signature is invalid."""
13+
pass
14+

requirements.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
asn1crypto==0.22.0
2+
certifi==2017.4.17
3+
cffi==1.10.0
4+
cryptography==1.8.2
5+
defusedxml==0.5.0
6+
eight==0.4.2
7+
future==0.16.0
8+
idna==2.5
9+
lxml==3.8.0
10+
mock==2.0.0
11+
packaging==16.8
12+
pbr==3.0.1
13+
py==1.4.34
14+
pycparser==2.17
15+
pyOpenSSL==17.0.0
16+
pyparsing==2.2.0
17+
pytest==3.1.1
18+
pytest-runner==2.11.1
19+
signxml==2.3.0
20+
six==1.10.0

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
],
1616
keywords='XAR',
1717
install_requires=[
18+
'signxml>=2.3.0'
1819
],
1920
python_requires='>=3.5',
2021
tests_require=[

testdata/simple/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.xar

testdata/simple/test_dir/test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Text File Content

0 commit comments

Comments
 (0)