scripts/x

branch
trunk
changeset 8
97388f5ff770
parent 7
1f3cb3845dfd
child 9
920417b8acc9
equal deleted inserted replaced
7:1f3cb3845dfd 8:97388f5ff770
243 def check_contents(self): 243 def check_contents(self):
244 TarExtractor.check_contents(self) 244 TarExtractor.check_contents(self)
245 return BOMB 245 return BOMB
246 246
247 247
248 class MatchHandler(object):
249 def __init__(self, extractor, contents):
250 self.extractor = extractor
251 self.contents = contents
252 self.directory = extractor.basename()
253
254 def extract(self, directory='.'):
255 try:
256 self.extractor.extract(directory)
257 except ExtractorError, error:
258 return error.strerror
259
260 def cleanup(self):
261 command = 'chmod'
262 status = subprocess.call(['chmod', '-R', 'u+rw', self.directory])
263 if status == 0:
264 command = 'find'
265 status = subprocess.call(['find', self.directory, '-type', 'd',
266 '-exec', 'chmod', 'u+x', '{}', ';'])
267 if status != 0:
268 return "%s returned with exit status %s" % (command, status)
269
270
271 class BombHandler(MatchHandler):
272 def __init__(self, extractor, contents):
273 MatchHandler.__init__(self, extractor, contents)
274 basename = self.directory
275 for suffix in [''] + ['.%s' % (x,) for x in range(1, 10)]:
276 self.directory = '%s%s' % (basename, suffix)
277 try:
278 os.mkdir(self.directory)
279 except OSError, error:
280 if error.errno == errno.EEXIST:
281 continue
282 raise ValueError("could not make extraction directory %s: %s" %
283 (error.filename, error.strerror))
284 ## if suffix != '':
285 ## self.show_error("extracted to %s" % (directory,))
286 break
287 else:
288 raise ValueError("all good names for an extraction directory taken")
289
290 def extract(self):
291 os.chdir(self.directory)
292 return MatchHandler.extract(self, '..')
293
294 def cleanup(self):
295 os.chdir('..')
296 return MatchHandler.cleanup(self)
297
298
299 class EmptyHandler(object):
300 def __init__(self, extractor, contents): pass
301 def extract(self): pass
302 def cleanup(self): pass
303
248 extractor_map = {'application/x-tar': TarExtractor, 304 extractor_map = {'application/x-tar': TarExtractor,
249 'application/zip': ZipExtractor, 305 'application/zip': ZipExtractor,
250 'application/x-msdos-program': ZipExtractor, 306 'application/x-msdos-program': ZipExtractor,
251 'application/x-debian-package': DebExtractor, 307 'application/x-debian-package': DebExtractor,
252 'application/x-redhat-package-manager': RPMExtractor, 308 'application/x-redhat-package-manager': RPMExtractor,
253 'application/x-rpm': RPMExtractor, 309 'application/x-rpm': RPMExtractor,
254 'application/x-cpio': CpioExtractor} 310 'application/x-cpio': CpioExtractor}
255 311
312 handler_map = {EMPTY: EmptyHandler,
313 MATCHING_DIRECTORY: MatchHandler}
314
256 class ExtractorApplication(object): 315 class ExtractorApplication(object):
257 actions = ['get_extractor', 'prepare_extraction', 'extract', 'recurse']
258
259 def __init__(self, arguments): 316 def __init__(self, arguments):
260 self.parse_options(arguments) 317 self.parse_options(arguments)
261 self.successes = [] 318 self.successes = []
262 self.failures = [] 319 self.failures = []
263 320
279 print >>sys.stderr, "%s: %s" % (self.current_filename, message) 336 print >>sys.stderr, "%s: %s" % (self.current_filename, message)
280 337
281 def get_extractor(self): 338 def get_extractor(self):
282 mimetype, encoding = mimetypes.guess_type(self.current_filename) 339 mimetype, encoding = mimetypes.guess_type(self.current_filename)
283 try: 340 try:
284 handler = extractor_map[mimetype] 341 extractor = extractor_map[mimetype]
285 except KeyError: 342 except KeyError:
286 self.show_error("not a known archive type") 343 return "not a known archive type"
287 return False 344 try:
288 try: 345 self.current_extractor = extractor(self.current_filename, mimetype,
289 self.current_extractor = handler(self.current_filename, mimetype, 346 encoding)
290 encoding) 347 content = self.current_extractor.check_contents()
348 handler = handler_map.get(content, BombHandler)
349 self.current_handler = handler(self.current_extractor, content)
291 except ExtractorError, error: 350 except ExtractorError, error:
351 return str(error)
352
353 def recurse(self):
354 if not self.options.recursive:
355 return
356 archive_path = os.path.split(self.current_filename)[0]
357 for filename in self.current_extractor.included_archives:
358 tail_path, basename = os.path.split(filename)
359 directory = os.path.join(self.current_directory, archive_path,
360 self.current_handler.directory, tail_path)
361 self.archives.setdefault(directory, []).append(basename)
362
363 def report(self, function, *args):
364 error = function(*args)
365 if error:
292 self.show_error(error) 366 self.show_error(error)
293 return False 367 return False
294 return True 368 return True
295
296 def prepare_target_directory(self):
297 basename = self.current_extractor.basename()
298 for suffix in [''] + ['.%s' % (x,) for x in range(1, 10)]:
299 directory = '%s%s' % (basename, suffix)
300 try:
301 os.mkdir(directory)
302 except OSError, error:
303 if error.errno == errno.EEXIST:
304 continue
305 self.show_error("could not create extraction directory %s: %s" %
306 (error.filename, error.strerror))
307 return None
308 if suffix != '':
309 self.show_error("extracted to %s" % (directory,))
310 break
311 else:
312 self.show_error("all good names for an extraction directory taken")
313 return directory
314
315 def move_to_directory(self, filename, target):
316 if not os.path.isdir(filename):
317 filename = os.path.split(filename)[0]
318 target = os.path.join(target, filename)
319 os.rename(filename, target)
320
321 def prepare_extraction(self):
322 self.current_path = '.'
323 contents = self.current_extractor.check_contents()
324 if contents == MATCHING_DIRECTORY:
325 self.target_directory = self.current_filename
326 elif contents != EMPTY:
327 self.target_directory = self.prepare_target_directory()
328 if self.target_directory is None:
329 return False
330 if contents == BOMB:
331 os.chdir(self.target_directory)
332 self.current_path = '..'
333 else:
334 self.cleanup_actions.append((self.move_to_directory, contents,
335 self.target_directory))
336 else:
337 self.target_directory = None
338 return True
339
340 def extract(self):
341 try:
342 self.current_extractor.extract(self.current_path)
343 except ExtractorError, error:
344 self.show_error(error)
345 return False
346 return True
347
348 def recurse(self):
349 if not self.options.recursive:
350 return True
351 for filename in self.current_extractor.included_archives:
352 tail_path, basename = os.path.split(filename)
353 directory = os.path.join(self.current_directory,
354 self.target_directory, tail_path)
355 self.archives.setdefault(directory, []).append(basename)
356 return True
357
358 def fix_perms(self):
359 if self.target_directory is None:
360 return True
361 status = subprocess.call(['chmod', '-R', 'u+rw',
362 os.path.join(self.current_directory,
363 self.target_directory)])
364 if status == 0:
365 status = subprocess.call(['find',
366 os.path.join(self.current_directory,
367 self.target_directory),
368 '-type', 'd',
369 '-exec', 'chmod', 'u+x', '{}', ';'])
370 return status == 0
371 369
372 def run(self): 370 def run(self):
373 while self.archives: 371 while self.archives:
374 self.current_directory, filenames = self.archives.popitem() 372 self.current_directory, filenames = self.archives.popitem()
375 for filename in filenames: 373 for filename in filenames:
376 os.chdir(self.current_directory) 374 os.chdir(self.current_directory)
377 running = True
378 self.current_filename = filename 375 self.current_filename = filename
379 self.cleanup_actions = [] 376 self.cleanup_actions = []
380 actions = [getattr(self, name) for name in self.actions] 377 success = self.report(self.get_extractor)
381 while running and actions: 378 if success:
382 running = actions.pop(0)() 379 for name in 'extract', 'cleanup':
383 for action in self.cleanup_actions: 380 success = (self.report(getattr(self.current_handler,
384 action[0](*action[1:]) 381 name)) and success)
385 running = self.fix_perms() 382 self.recurse()
386 if running: 383 if success:
387 self.successes.append(self.current_filename) 384 self.successes.append(self.current_filename)
388 else: 385 else:
389 self.failures.append(self.current_filename) 386 self.failures.append(self.current_filename)
390 if self.failures: 387 if self.failures:
391 return 1 388 return 1

mercurial