68 |
68 |
69 class FilenameChecker(object): |
69 class FilenameChecker(object): |
70 def __init__(self, original_name): |
70 def __init__(self, original_name): |
71 self.original_name = original_name |
71 self.original_name = original_name |
72 |
72 |
73 def is_free(self, filename=None): |
73 def is_free(self, filename): |
74 if filename is None: |
|
75 filename = self.original_name |
|
76 return self._is_free(filename) |
|
77 |
|
78 def _is_free(self, filename): |
|
79 return not os.path.exists(filename) |
74 return not os.path.exists(filename) |
80 |
75 |
81 def check(self): |
76 def check(self): |
82 for suffix in [''] + ['.%s' % (x,) for x in range(1, 10)]: |
77 for suffix in [''] + ['.%s' % (x,) for x in range(1, 10)]: |
83 filename = '%s%s' % (self.original_name, suffix) |
78 filename = '%s%s' % (self.original_name, suffix) |
86 raise ValueError("all alternatives for name %s taken" % |
81 raise ValueError("all alternatives for name %s taken" % |
87 (self.original_name,)) |
82 (self.original_name,)) |
88 |
83 |
89 |
84 |
90 class DirectoryChecker(FilenameChecker): |
85 class DirectoryChecker(FilenameChecker): |
91 def _is_free(self, filename): |
86 def is_free(self, filename): |
92 try: |
87 try: |
93 os.mkdir(filename) |
88 os.mkdir(filename) |
94 except OSError, error: |
89 except OSError, error: |
95 if error.errno == errno.EEXIST: |
90 if error.errno == errno.EEXIST: |
96 return False |
91 return False |
320 def __init__(self, extractor, contents, options): |
315 def __init__(self, extractor, contents, options): |
321 self.logger = logging.getLogger('x-log') |
316 self.logger = logging.getLogger('x-log') |
322 self.extractor = extractor |
317 self.extractor = extractor |
323 self.contents = contents |
318 self.contents = contents |
324 self.options = options |
319 self.options = options |
325 |
320 self.target = None |
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 |
321 |
339 def extract(self): |
322 def extract(self): |
340 try: |
323 try: |
341 self.extractor.extract(self.target) |
324 self.extractor.extract(self.target) |
342 except ExtractorError, error: |
325 except (ExtractorError, IOError, OSError), error: |
343 return str(error) |
326 return str(error) |
344 |
327 |
345 def cleanup(self): |
328 def cleanup(self): |
346 raise NotImplementedError |
329 if self.target is None: |
347 if self.options.flat: |
330 return |
348 self.cleanup_files() |
|
349 else: |
|
350 self.cleanup_directory() |
|
351 |
|
352 def cleanup_files(self): |
|
353 for filename in self.extractor.get_filenames(): |
|
354 stat_info = os.stat(filename) |
|
355 perms = stat.S_IRUSR | stat.S_IWUSR |
|
356 if stat.S_ISDIR(stat_info.st_mode): |
|
357 perms |= stat.S_IXUSR |
|
358 os.chmod(filename, stat_info.st_mode | perms) |
|
359 |
|
360 def cleanup_directory(self): |
|
361 command = 'find' |
331 command = 'find' |
362 status = subprocess.call(['find', self.target, '-type', 'd', |
332 status = subprocess.call(['find', self.target, '-type', 'd', |
363 '-exec', 'chmod', 'u+rwx', '{}', ';']) |
333 '-exec', 'chmod', 'u+rwx', '{}', ';']) |
364 if status == 0: |
334 if status == 0: |
365 command = 'chmod' |
335 command = 'chmod' |
374 # Flat Overwrite None |
344 # Flat Overwrite None |
375 # File basename basename FilenameChecked |
345 # File basename basename FilenameChecked |
376 # Match . . tempdir + checked |
346 # Match . . tempdir + checked |
377 # Bomb . basename DirectoryChecked |
347 # Bomb . basename DirectoryChecked |
378 |
348 |
379 |
|
380 class FlatHandler(BaseHandler): |
349 class FlatHandler(BaseHandler): |
381 def can_handle(contents, options): |
350 def can_handle(contents, options): |
382 return ((options.flat and (contents != COMPRESSED)) or |
351 return ((options.flat and (contents != COMPRESSED)) or |
383 (options.overwrite and (contents == MATCHING_DIRECTORY))) |
352 (options.overwrite and (contents == MATCHING_DIRECTORY))) |
384 can_handle = staticmethod(can_handle) |
353 can_handle = staticmethod(can_handle) |
385 |
354 |
386 target = '.' |
355 def __init__(self, extractor, contents, options): |
387 cleanup = BaseHandler.cleanup_files |
356 BaseHandler.__init__(self, extractor, contents, options) |
|
357 self.target = '.' |
|
358 |
|
359 def cleanup(self): |
|
360 for filename in self.extractor.get_filenames(): |
|
361 stat_info = os.stat(filename) |
|
362 perms = stat.S_IRUSR | stat.S_IWUSR |
|
363 if stat.S_ISDIR(stat_info.st_mode): |
|
364 perms |= stat.S_IXUSR |
|
365 os.chmod(filename, stat_info.st_mode | perms) |
388 |
366 |
389 |
367 |
390 class OverwriteHandler(BaseHandler): |
368 class OverwriteHandler(BaseHandler): |
391 def can_handle(contents, options): |
369 def can_handle(contents, options): |
392 return ((options.flat and (contents == COMPRESSED)) or |
370 return ((options.flat and (contents == COMPRESSED)) or |
394 can_handle = staticmethod(can_handle) |
372 can_handle = staticmethod(can_handle) |
395 |
373 |
396 def __init__(self, extractor, contents, options): |
374 def __init__(self, extractor, contents, options): |
397 BaseHandler.__init__(self, extractor, contents, options) |
375 BaseHandler.__init__(self, extractor, contents, options) |
398 self.target = self.extractor.basename() |
376 self.target = self.extractor.basename() |
399 |
|
400 cleanup = BaseHandler.cleanup_directory |
|
401 |
377 |
402 |
378 |
403 class MatchHandler(BaseHandler): |
379 class MatchHandler(BaseHandler): |
404 def can_handle(contents, options): |
380 def can_handle(contents, options): |
405 return contents == MATCHING_DIRECTORY |
381 return contents == MATCHING_DIRECTORY |
406 can_handle = staticmethod(can_handle) |
382 can_handle = staticmethod(can_handle) |
407 |
383 |
408 def extract(self): |
384 def extract(self): |
409 basename = self.extractor.basename() |
385 basename = self.extractor.basename() |
410 self.target = tempfile.mkdtemp() |
386 self.target = tempfile.mkdtemp(dir='.') |
411 result = BaseHandler.extract(self) |
387 result = BaseHandler.extract(self) |
412 if result is None: |
388 if result is None: |
413 tempdir = self.target |
389 tempdir = self.target |
414 checker = self.extractor.name_checker(basename) |
390 checker = self.extractor.name_checker(basename) |
415 self.target = checker.check() |
391 self.target = checker.check() |
416 os.rename(os.path.join(tempdir, basename), self.target) |
392 os.rename(os.path.join(tempdir, basename), self.target) |
417 os.rmdir(tempdir) |
393 os.rmdir(tempdir) |
418 return result |
394 return result |
419 |
395 |
420 cleanup = BaseHandler.cleanup_directory |
|
421 |
|
422 |
396 |
423 class EmptyHandler(object): |
397 class EmptyHandler(object): |
424 def can_handle(contents, options): |
398 def can_handle(contents, options): |
425 return contents == EMPTY |
399 return contents == EMPTY |
426 can_handle = staticmethod(can_handle) |
400 can_handle = staticmethod(can_handle) |
438 def __init__(self, extractor, contents, options): |
412 def __init__(self, extractor, contents, options): |
439 BaseHandler.__init__(self, extractor, contents, options) |
413 BaseHandler.__init__(self, extractor, contents, options) |
440 checker = self.extractor.name_checker(self.extractor.basename()) |
414 checker = self.extractor.name_checker(self.extractor.basename()) |
441 self.target = checker.check() |
415 self.target = checker.check() |
442 |
416 |
443 cleanup = BaseHandler.cleanup_directory |
417 |
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) |
|
453 |
|
454 |
|
455 extractor_map = {'application/x-tar': TarExtractor, |
418 extractor_map = {'application/x-tar': TarExtractor, |
456 'application/zip': ZipExtractor, |
419 'application/zip': ZipExtractor, |
457 'application/x-msdos-program': ZipExtractor, |
420 'application/x-msdos-program': ZipExtractor, |
458 'application/x-debian-package': DebExtractor, |
421 'application/x-debian-package': DebExtractor, |
459 'application/x-redhat-package-manager': RPMExtractor, |
422 'application/x-redhat-package-manager': RPMExtractor, |
538 directory = os.path.join(self.current_directory, |
501 directory = os.path.join(self.current_directory, |
539 self.current_handler.target, tail_path) |
502 self.current_handler.target, tail_path) |
540 self.archives.setdefault(directory, []).append(basename) |
503 self.archives.setdefault(directory, []).append(basename) |
541 |
504 |
542 def report(self, function, *args): |
505 def report(self, function, *args): |
543 error = function(*args) |
506 try: |
|
507 error = function(*args) |
|
508 except (ExtractorError, IOError, OSError), exception: |
|
509 error = str(exception) |
544 if error: |
510 if error: |
545 self.logger.error("%s: %s", self.current_filename, error) |
511 self.logger.error("%s: %s", self.current_filename, error) |
546 return False |
512 return False |
547 return True |
513 return True |
548 |
514 |
550 while self.archives: |
516 while self.archives: |
551 self.current_directory, filenames = self.archives.popitem() |
517 self.current_directory, filenames = self.archives.popitem() |
552 for filename in filenames: |
518 for filename in filenames: |
553 os.chdir(self.current_directory) |
519 os.chdir(self.current_directory) |
554 self.current_filename = filename |
520 self.current_filename = filename |
555 self.cleanup_actions = [] |
|
556 success = self.report(self.get_extractor) |
521 success = self.report(self.get_extractor) |
557 if success: |
522 if success: |
558 for name in 'extract', 'cleanup': |
523 for name in 'extract', 'cleanup': |
559 success = (self.report(getattr(self.current_handler, |
524 success = (self.report(getattr(self.current_handler, |
560 name)) and success) |
525 name)) and success) |