321 self.logger = logging.getLogger('x-log') |
321 self.logger = logging.getLogger('x-log') |
322 self.extractor = extractor |
322 self.extractor = extractor |
323 self.contents = contents |
323 self.contents = contents |
324 self.options = options |
324 self.options = options |
325 |
325 |
|
326 ## def extract(self): |
|
327 ## raise NotImplementedError |
|
328 ## checker = self.extractor.name_checker(self.extractor.basename()) |
|
329 ## if self.options.flat: |
|
330 ## self.target = '.' |
|
331 ## return self.do_extract('.') |
|
332 ## elif self.options.overwrite or checker.is_free(): |
|
333 ## self.target = self.extractor.basename() |
|
334 ## return self.overwrite() |
|
335 ## else: |
|
336 ## self.target = checker.check() |
|
337 ## return self.safe_extract() |
|
338 |
326 def extract(self): |
339 def extract(self): |
327 checker = self.extractor.name_checker(self.extractor.basename()) |
340 try: |
328 if self.options.flat: |
341 self.extractor.extract(self.target) |
329 self.target = '.' |
|
330 return self.do_extract('.') |
|
331 elif self.options.overwrite or checker.is_free(): |
|
332 self.target = self.extractor.basename() |
|
333 return self.overwrite() |
|
334 else: |
|
335 self.target = checker.check() |
|
336 return self.safe_extract() |
|
337 |
|
338 def do_extract(self, directory): |
|
339 try: |
|
340 self.extractor.extract(directory) |
|
341 except ExtractorError, error: |
342 except ExtractorError, error: |
342 return str(error) |
343 return str(error) |
343 |
344 |
344 def cleanup(self): |
345 def cleanup(self): |
|
346 raise NotImplementedError |
345 if self.options.flat: |
347 if self.options.flat: |
346 self.cleanup_files() |
348 self.cleanup_files() |
347 else: |
349 else: |
348 self.cleanup_directory() |
350 self.cleanup_directory() |
349 |
351 |
364 status = subprocess.call(['chmod', '-R', 'u+rw', self.target]) |
366 status = subprocess.call(['chmod', '-R', 'u+rw', self.target]) |
365 if status != 0: |
367 if status != 0: |
366 return "%s returned with exit status %s" % (command, status) |
368 return "%s returned with exit status %s" % (command, status) |
367 |
369 |
368 |
370 |
|
371 # The "where to extract" table, with options and archive types. |
|
372 # This dictates the contents of each can_handle method. |
|
373 # |
|
374 # Flat Overwrite None |
|
375 # File basename basename FilenameChecked |
|
376 # Match . . tempdir + checked |
|
377 # Bomb . basename DirectoryChecked |
|
378 |
|
379 |
|
380 class FlatHandler(BaseHandler): |
|
381 def can_handle(contents, options): |
|
382 return ((options.flat and (contents != COMPRESSED)) or |
|
383 (options.overwrite and (contents == MATCHING_DIRECTORY))) |
|
384 can_handle = staticmethod(can_handle) |
|
385 |
|
386 target = '.' |
|
387 cleanup = BaseHandler.cleanup_files |
|
388 |
|
389 |
|
390 class OverwriteHandler(BaseHandler): |
|
391 def can_handle(contents, options): |
|
392 return ((options.flat and (contents == COMPRESSED)) or |
|
393 (options.overwrite and (contents != MATCHING_DIRECTORY))) |
|
394 can_handle = staticmethod(can_handle) |
|
395 |
|
396 def __init__(self, extractor, contents, options): |
|
397 BaseHandler.__init__(self, extractor, contents, options) |
|
398 self.target = self.extractor.basename() |
|
399 |
|
400 cleanup = BaseHandler.cleanup_directory |
|
401 |
|
402 |
369 class MatchHandler(BaseHandler): |
403 class MatchHandler(BaseHandler): |
370 def overwrite(self): |
404 def can_handle(contents, options): |
371 return self.do_extract('.') |
405 return contents == MATCHING_DIRECTORY |
372 |
406 can_handle = staticmethod(can_handle) |
373 def safe_extract(self): |
407 |
374 tempdir = tempfile.mkdtemp() |
408 def extract(self): |
375 result = self.do_extract(tempdir) |
409 basename = self.extractor.basename() |
|
410 self.target = tempfile.mkdtemp() |
|
411 result = BaseHandler.extract(self) |
376 if result is None: |
412 if result is None: |
377 os.rename(os.path.join(tempdir, self.extractor.basename()), |
413 tempdir = self.target |
378 self.target) |
414 checker = self.extractor.name_checker(basename) |
|
415 self.target = checker.check() |
|
416 os.rename(os.path.join(tempdir, basename), self.target) |
379 os.rmdir(tempdir) |
417 os.rmdir(tempdir) |
380 return result |
418 return result |
381 |
419 |
382 |
420 cleanup = BaseHandler.cleanup_directory |
383 class BombHandler(BaseHandler): |
|
384 def safe_extract(self): |
|
385 return self.do_extract(self.target) |
|
386 |
|
387 def overwrite(self): |
|
388 self.target = self.extractor.basename() |
|
389 return self.do_extract(self.target) |
|
390 |
421 |
391 |
422 |
392 class EmptyHandler(object): |
423 class EmptyHandler(object): |
|
424 def can_handle(contents, options): |
|
425 return contents == EMPTY |
|
426 can_handle = staticmethod(can_handle) |
|
427 |
393 def __init__(self, extractor, contents, options): pass |
428 def __init__(self, extractor, contents, options): pass |
394 def extract(self): pass |
429 def extract(self): pass |
395 def cleanup(self): pass |
430 def cleanup(self): pass |
|
431 |
|
432 |
|
433 class BombHandler(BaseHandler): |
|
434 def can_handle(contents, options): |
|
435 return True |
|
436 can_handle = staticmethod(can_handle) |
|
437 |
|
438 def __init__(self, extractor, contents, options): |
|
439 BaseHandler.__init__(self, extractor, contents, options) |
|
440 checker = self.extractor.name_checker(self.extractor.basename()) |
|
441 self.target = checker.check() |
|
442 |
|
443 cleanup = BaseHandler.cleanup_directory |
|
444 |
|
445 |
|
446 ## class BombHandler(BaseHandler): |
|
447 ## def safe_extract(self): |
|
448 ## return self.do_extract(self.target) |
|
449 |
|
450 ## def overwrite(self): |
|
451 ## self.target = self.extractor.basename() |
|
452 ## return self.do_extract(self.target) |
396 |
453 |
397 |
454 |
398 extractor_map = {'application/x-tar': TarExtractor, |
455 extractor_map = {'application/x-tar': TarExtractor, |
399 'application/zip': ZipExtractor, |
456 'application/zip': ZipExtractor, |
400 'application/x-msdos-program': ZipExtractor, |
457 'application/x-msdos-program': ZipExtractor, |
401 'application/x-debian-package': DebExtractor, |
458 'application/x-debian-package': DebExtractor, |
402 'application/x-redhat-package-manager': RPMExtractor, |
459 'application/x-redhat-package-manager': RPMExtractor, |
403 'application/x-rpm': RPMExtractor, |
460 'application/x-rpm': RPMExtractor, |
404 'application/x-cpio': CpioExtractor} |
461 'application/x-cpio': CpioExtractor} |
405 |
462 |
406 handler_map = {EMPTY: EmptyHandler, |
463 handlers = [FlatHandler, OverwriteHandler, MatchHandler, EmptyHandler, |
407 MATCHING_DIRECTORY: MatchHandler, |
464 BombHandler] |
408 DECOMPRESSED: BombHandler} |
|
409 |
465 |
410 class ExtractorApplication(object): |
466 class ExtractorApplication(object): |
411 def __init__(self, arguments): |
467 def __init__(self, arguments): |
412 self.parse_options(arguments) |
468 self.parse_options(arguments) |
413 self.setup_logger() |
469 self.setup_logger() |
457 try: |
513 try: |
458 extractor = extractor_map[mimetype] |
514 extractor = extractor_map[mimetype] |
459 except KeyError: |
515 except KeyError: |
460 if encoding: |
516 if encoding: |
461 extractor = CompressionExtractor |
517 extractor = CompressionExtractor |
|
518 contents = COMPRESSED |
462 else: |
519 else: |
463 return "not a known archive type" |
520 return "not a known archive type" |
464 try: |
521 try: |
465 self.current_extractor = extractor(self.current_filename, mimetype, |
522 self.current_extractor = extractor(self.current_filename, mimetype, |
466 encoding) |
523 encoding) |
467 content = self.current_extractor.check_contents() |
524 content = self.current_extractor.check_contents() |
468 handler = handler_map.get(content, BombHandler) |
525 for handler in handlers: |
469 self.current_handler = handler(self.current_extractor, content, |
526 if handler.can_handle(content, self.options): |
470 self.options) |
527 self.current_handler = handler(self.current_extractor, |
|
528 content, self.options) |
|
529 break |
471 except ExtractorError, error: |
530 except ExtractorError, error: |
472 return str(error) |
531 return str(error) |
473 |
532 |
474 def recurse(self): |
533 def recurse(self): |
475 if not self.options.recursive: |
534 if not self.options.recursive: |