Commit 1665aeea56540af9923a5dae996327d9d0ae8b5a

Authored by Christian Herdtweck
1 parent 7680eb11

oleobj: vary shell status code if dumped or not / error occurred

Tell caller of script roughly what happened in call.

Also: check whether given file arguments exist and return non-zero exit
and remove print of non-existent __doc__
Showing 1 changed file with 45 additions and 10 deletions
oletools/oleobj.py
... ... @@ -165,6 +165,13 @@ STR_MAX_LEN = 1024
165 165 # size of chunks to copy from ole stream to file
166 166 DUMP_CHUNK_SIZE = 4096
167 167  
  168 +# return values from main; can be added
  169 +# (e.g.: did dump but had err parsing and dumping --> return 1+4+8 = 13)
  170 +RETURN_NO_DUMP = 0 # nothing found to dump/extract
  171 +RETURN_DID_DUMP = 1 # did dump/extract successfully
  172 +RETURN_ERR_ARGS = 2 # reserve for OptionParser.parse_args
  173 +RETURN_ERR_STREAM = 4 # error opening/parsing a stream
  174 +RETURN_ERR_DUMP = 8 # error dumping data from stream to file
168 175  
169 176 # === FUNCTIONS ==============================================================
170 177  
... ... @@ -527,12 +534,14 @@ def process_file(container, filename, data, output_dir=None):
527 534 print ('File: %r' % filename)
528 535 index = 1
529 536  
  537 + # do not throw errors but remember them and try continue with other streams
  538 + err_stream = False
  539 + err_dumping = False
  540 + did_dump = False
  541 +
530 542 # look for ole files inside file (e.g. unzip docx)
531   - flag_no_ole = False
532   - flag_stream_error = False
533 543 for ole in find_ole(filename, data):
534 544 if ole is None: # no ole file found
535   - flag_no_ole = True
536 545 continue
537 546  
538 547 for path_parts in ole.listdir():
... ... @@ -542,13 +551,14 @@ def process_file(container, filename, data, output_dir=None):
542 551 stream = None
543 552 try:
544 553 stream = ole.openstream(path_parts)
545   - print('extract file embedded in OLE object from stream %r:' % stream_path)
  554 + print('extract file embedded in OLE object from stream %r:'
  555 + % stream_path)
546 556 print ('Parsing OLE Package')
547 557 opkg = OleNativeStream(stream)
548 558 # leave stream open until dumping is finished
549 559 except Exception:
550 560 log.warning('*** Not an OLE 1.0 Object ({0})'.format(exc))
551   - flag_stream_error = True
  561 + err_stream = True
552 562 if stream is not None:
553 563 stream.close()
554 564 continue
... ... @@ -583,18 +593,22 @@ def process_file(container, filename, data, output_dir=None):
583 593 break
584 594 next_size = min(DUMP_CHUNK_SIZE,
585 595 opkg.actual_size - n_dumped)
  596 + did_dump = True
586 597 except Exception as exc:
587 598 log.warning('error dumping to {0} ({1})'
588 599 .format(fname, exc))
  600 + err_dumping = True
589 601 finally:
590 602 stream.close()
591 603  
592 604 index += 1
  605 + return err_stream, err_dumping, did_dump
593 606  
594 607  
595 608 #=== MAIN =================================================================
596 609  
597 610 def main():
  611 + """ main function, called when running this as script """
598 612 # print banner with version
599 613 print ('oleobj %s - http://decalage.info/oletools' % __version__)
600 614 print ('THIS IS WORK IN PROGRESS - Check updates regularly!')
... ... @@ -640,9 +654,12 @@ def main():
640 654  
641 655 # Print help if no arguments are passed
642 656 if len(args) == 0:
643   - print (__doc__)
644 657 parser.print_help()
645   - sys.exit()
  658 + return RETURN_ERR_ARGS
  659 + for filename in args:
  660 + if not os.path.isfile(filename):
  661 + print('File does not exist: ' + filename)
  662 + return RETURN_ERR_ARGS
646 663  
647 664 # Setup logging to the console:
648 665 # here we use stdout instead of stderr by default, so that the output
... ... @@ -652,14 +669,32 @@ def main():
652 669 # enable logging in the modules:
653 670 log.setLevel(logging.NOTSET)
654 671  
  672 + # remember if there was a problem and continue with other data
  673 + any_err_stream = False
  674 + any_err_dumping = False
  675 + any_did_dump = False
655 676  
656 677 for container, filename, data in xglob.iter_files(args, recursive=options.recursive,
657 678 zip_password=options.zip_password, zip_fname=options.zip_fname):
658 679 # ignore directory names stored in zip files:
659 680 if container and filename.endswith('/'):
660 681 continue
661   - process_file(container, filename, data, options.output_dir)
  682 + err_stream, err_dumping, did_dump = \
  683 + process_file(container, filename, data, options.output_dir)
  684 + any_err_stream |= err_stream
  685 + any_err_dumping |= err_dumping
  686 + any_did_dump |= did_dump
  687 +
  688 + # assemble return value
  689 + return_val = RETURN_NO_DUMP
  690 + if any_did_dump:
  691 + return_val += RETURN_DID_DUMP
  692 + if any_err_stream:
  693 + return_val += RETURN_ERR_STREAM
  694 + if any_err_dumping:
  695 + return_val += RETURN_ERR_DUMP
  696 + return return_val
662 697  
663   -if __name__ == '__main__':
664   - main()
665 698  
  699 +if __name__ == '__main__':
  700 + sys.exit(main())
... ...