Use correct exception in unrar check
[rarfile.git] / dumprar.py
blobada9370c7ab29b4c83da5864657f6a5b4f853fd1
1 #! /usr/bin/env python
3 """Dump archive contents, test extraction."""
5 import sys
6 import rarfile as rf
7 from binascii import crc32, hexlify
8 from datetime import datetime
10 try:
11 bytearray
12 except NameError:
13 import array
14 def bytearray(v):
15 return array.array('B', v)
17 rf.UNICODE_COMMENTS = 1
18 rf.USE_DATETIME = 1
20 usage = """
21 dumprar [switches] [ARC1 ARC2 ...] [@ARCLIST]
22 switches:
23 @file read archive names from file
24 -pPSW set password
25 -Ccharset set fallback charset
26 -v increase verbosity
27 -t attemt to read all files
28 -x write read files out
29 -c show archive comment
30 -h show usage
31 -- stop switch parsing
32 """.strip()
34 os_list = ['DOS', 'OS2', 'WIN', 'UNIX', 'MACOS', 'BEOS']
36 block_strs = ['MARK', 'MAIN', 'FILE', 'OLD_COMMENT', 'OLD_EXTRA',
37 'OLD_SUB', 'OLD_RECOVERY', 'OLD_AUTH', 'SUB', 'ENDARC']
39 def rarType(type):
40 if type < rf.RAR_BLOCK_MARK or type > rf.RAR_BLOCK_ENDARC:
41 return "*UNKNOWN*"
42 return block_strs[type - rf.RAR_BLOCK_MARK]
44 main_bits = (
45 (rf.RAR_MAIN_VOLUME, "VOL"),
46 (rf.RAR_MAIN_COMMENT, "COMMENT"),
47 (rf.RAR_MAIN_LOCK, "LOCK"),
48 (rf.RAR_MAIN_SOLID, "SOLID"),
49 (rf.RAR_MAIN_NEWNUMBERING, "NEWNR"),
50 (rf.RAR_MAIN_AUTH, "AUTH"),
51 (rf.RAR_MAIN_RECOVERY, "RECOVERY"),
52 (rf.RAR_MAIN_PASSWORD, "PASSWORD"),
53 (rf.RAR_MAIN_FIRSTVOLUME, "FIRSTVOL"),
54 (rf.RAR_SKIP_IF_UNKNOWN, "SKIP"),
55 (rf.RAR_LONG_BLOCK, "LONG"),
58 endarc_bits = (
59 (rf.RAR_ENDARC_NEXT_VOLUME, "NEXTVOL"),
60 (rf.RAR_ENDARC_DATACRC, "DATACRC"),
61 (rf.RAR_ENDARC_REVSPACE, "REVSPACE"),
62 (rf.RAR_ENDARC_VOLNR, "VOLNR"),
63 (rf.RAR_SKIP_IF_UNKNOWN, "SKIP"),
64 (rf.RAR_LONG_BLOCK, "LONG"),
67 file_bits = (
68 (rf.RAR_FILE_SPLIT_BEFORE, "SPLIT_BEFORE"),
69 (rf.RAR_FILE_SPLIT_AFTER, "SPLIT_AFTER"),
70 (rf.RAR_FILE_PASSWORD, "PASSWORD"),
71 (rf.RAR_FILE_COMMENT, "COMMENT"),
72 (rf.RAR_FILE_SOLID, "SOLID"),
73 (rf.RAR_FILE_LARGE, "LARGE"),
74 (rf.RAR_FILE_UNICODE, "UNICODE"),
75 (rf.RAR_FILE_SALT, "SALT"),
76 (rf.RAR_FILE_VERSION, "VERSION"),
77 (rf.RAR_FILE_EXTTIME, "EXTTIME"),
78 (rf.RAR_FILE_EXTFLAGS, "EXTFLAGS"),
79 (rf.RAR_SKIP_IF_UNKNOWN, "SKIP"),
80 (rf.RAR_LONG_BLOCK, "LONG"),
83 generic_bits = (
84 (rf.RAR_SKIP_IF_UNKNOWN, "SKIP"),
85 (rf.RAR_LONG_BLOCK, "LONG"),
88 file_parms = ("D64", "D128", "D256", "D512",
89 "D1024", "D2048", "D4096", "DIR")
91 def xprint(m):
92 if sys.hexversion < 0x3000000:
93 if isinstance(m, unicode):
94 m = m.encode('utf8')
95 sys.stdout.write(m)
96 sys.stdout.write('\n')
98 def render_flags(flags, bit_list):
99 res = []
100 known = 0
101 for bit in bit_list:
102 known = known | bit[0]
103 if flags & bit[0]:
104 res.append(bit[1])
105 unknown = flags & ~known
106 n = 0
107 while unknown:
108 if unknown & 1:
109 res.append("UNK_%04x" % (1 << n))
110 unknown = unknown >> 1
111 n += 1
113 return ",".join(res)
115 def get_file_flags(flags):
116 res = render_flags(flags & ~rf.RAR_FILE_DICTMASK, file_bits)
118 xf = (flags & rf.RAR_FILE_DICTMASK) >> 5
119 res += "," + file_parms[xf]
120 return res
122 def get_main_flags(flags):
123 return render_flags(flags, main_bits)
125 def get_endarc_flags(flags):
126 return render_flags(flags, endarc_bits)
128 def get_generic_flags(flags):
129 return render_flags(flags, generic_bits)
131 def fmt_time(t):
132 if isinstance(t, datetime):
133 return t.isoformat(' ')
134 return "%04d-%02d-%02d %02d:%02d:%02d" % t
136 def show_item(h):
137 st = rarType(h.type)
138 unknown = h.header_size - h.header_base
139 xprint("%s: hdrlen=%d datlen=%d hdr_unknown=%d" % (st, h.header_size,
140 h.add_size, unknown))
141 if unknown > 0 and cf_verbose > 1:
142 dat = h.header_data[h.header_base : ]
143 xprint(" unknown: %s" % hexlify(dat))
144 if h.type in (rf.RAR_BLOCK_FILE, rf.RAR_BLOCK_SUB):
145 if h.host_os == rf.RAR_OS_UNIX:
146 s_mode = "0%o" % h.mode
147 else:
148 s_mode = "0x%x" % h.mode
149 xprint(" flags=0x%04x:%s" % (h.flags, get_file_flags(h.flags)))
150 if h.host_os >= 0 and h.host_os < len(os_list):
151 s_os = os_list[h.host_os]
152 else:
153 s_os = "?"
154 xprint(" os=%d:%s ver=%d mode=%s meth=%c cmp=%d dec=%d vol=%d" % (
155 h.host_os, s_os,
156 h.extract_version, s_mode, h.compress_type,
157 h.compress_size, h.file_size, h.volume))
158 ucrc = (h.CRC + (1 << 32)) & ((1 << 32) - 1)
159 xprint(" crc=0x%08x (%d) time=%s" % (ucrc, h.CRC, fmt_time(h.date_time)))
160 xprint(" name=%s" % h.filename)
161 if h.mtime:
162 xprint(" mtime=%s" % fmt_time(h.mtime))
163 if h.ctime:
164 xprint(" ctime=%s" % fmt_time(h.ctime))
165 if h.atime:
166 xprint(" atime=%s" % fmt_time(h.atime))
167 if h.arctime:
168 xprint(" arctime=%s" % fmt_time(h.arctime))
169 elif h.type == rf.RAR_BLOCK_MAIN:
170 xprint(" flags=0x%04x:%s" % (h.flags, get_main_flags(h.flags)))
171 elif h.type == rf.RAR_BLOCK_ENDARC:
172 xprint(" flags=0x%04x:%s" % (h.flags, get_endarc_flags(h.flags)))
173 elif h.type == rf.RAR_BLOCK_MARK:
174 xprint(" flags=0x%04x:" % (h.flags,))
175 else:
176 xprint(" flags=0x%04x:%s" % (h.flags, get_generic_flags(h.flags)))
178 if h.comment is not None:
179 cm = repr(h.comment)
180 if cm[0] == 'u':
181 cm = cm[1:]
182 xprint(" comment=%s" % cm)
184 cf_show_comment = 0
185 cf_verbose = 0
186 cf_charset = None
187 cf_extract = 0
188 cf_test_read = 0
189 cf_test_unrar = 0
191 def check_crc(f, inf):
192 ucrc = f.CRC
193 if ucrc < 0:
194 ucrc += (long(1) << 32)
195 if ucrc != inf.CRC:
196 print ('crc error')
198 def test_read_long(r, inf):
199 f = r.open(inf.filename)
200 total = 0
201 while 1:
202 data = f.read(8192)
203 if not data:
204 break
205 total += len(data)
206 if total != inf.file_size:
207 xprint("\n *** %s has corrupt file: %s ***" % (r.rarfile, inf.filename))
208 xprint(" *** short read: got=%d, need=%d ***\n" % (total, inf.file_size))
209 check_crc(f, inf)
211 # test .seek() & .readinto()
212 if cf_test_read > 1:
213 f.seek(0,0)
215 # hack: re-enable crc calc
216 f.crc_check = 1
217 f.CRC = 0
219 total = 0
220 buf = bytearray(rf.ZERO*4096)
221 while 1:
222 res = f.readinto(buf)
223 if not res:
224 break
225 total += res
226 if inf.file_size != total:
227 xprint(" *** readinto failed: got=%d, need=%d ***\n" % (total, inf.file_size))
228 check_crc(f, inf)
229 f.close()
231 def test_read(r, inf):
232 test_read_long(r, inf)
235 def test_real(fn, psw):
236 xprint("Archive: %s" % fn)
238 cb = None
239 if cf_verbose > 1:
240 cb = show_item
242 # check if rar
243 if not rf.is_rarfile(fn):
244 xprint(" --- %s is not a RAR file ---" % fn)
245 return
247 # open
248 r = rf.RarFile(fn, charset = cf_charset, info_callback = cb)
249 # set password
250 if r.needs_password():
251 if psw:
252 r.setpassword(psw)
253 else:
254 xprint(" --- %s requires password ---" % fn)
255 return
257 # show comment
258 if cf_show_comment and r.comment:
259 for ln in r.comment.split('\n'):
260 xprint(" %s" % ln)
261 elif cf_verbose == 1 and r.comment:
262 cm = repr(r.comment)
263 if cm[0] == 'u':
264 cm = cm[1:]
265 xprint(" comment=%s" % cm)
267 # process
268 for n in r.namelist():
269 inf = r.getinfo(n)
270 if inf.isdir():
271 continue
272 if cf_verbose == 1:
273 show_item(inf)
274 if cf_test_read:
275 test_read(r, inf)
277 if cf_extract:
278 r.extractall()
279 for inf in r.infolist():
280 r.extract(inf)
282 if cf_test_unrar:
283 r.testrar()
285 def test(fn, psw):
286 try:
287 test_real(fn, psw)
288 except rf.NeedFirstVolume:
289 xprint(" --- %s is middle part of multi-vol archive ---" % fn)
290 except rf.Error:
291 exc, msg, tb = sys.exc_info()
292 xprint("\n *** %s: %s ***\n" % (exc.__name__, msg))
293 del tb
294 except IOError:
295 exc, msg, tb = sys.exc_info()
296 xprint("\n *** %s: %s ***\n" % (exc.__name__, msg))
297 del tb
299 def main():
300 global cf_verbose, cf_show_comment, cf_charset
301 global cf_extract, cf_test_read, cf_test_unrar
303 # parse args
304 args = []
305 psw = None
306 noswitch = False
307 for a in sys.argv[1:]:
308 if noswitch:
309 args.append(a)
310 elif a[0] == "@":
311 for ln in open(a[1:], 'r'):
312 fn = ln[:-1]
313 args.append(fn)
314 elif a[0] != '-':
315 args.append(a)
316 elif a[1] == 'p':
317 psw = a[2:]
318 elif a == '--':
319 noswitch = True
320 elif a == '-h':
321 xprint(usage)
322 return
323 elif a == '-v':
324 cf_verbose += 1
325 elif a == '-c':
326 cf_show_comment = 1
327 elif a == '-x':
328 cf_extract = 1
329 elif a == '-t':
330 cf_test_read += 1
331 elif a == '-T':
332 cf_test_unrar = 1
333 elif a[1] == 'C':
334 cf_charset = a[2:]
335 else:
336 raise Exception("unknown switch: "+a)
337 if not args:
338 xprint(usage)
340 for fn in args:
341 test(fn, psw)
344 if __name__ == '__main__':
345 try:
346 main()
347 except KeyboardInterrupt:
348 pass