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,6 +165,13 @@ STR_MAX_LEN = 1024
165 # size of chunks to copy from ole stream to file 165 # size of chunks to copy from ole stream to file
166 DUMP_CHUNK_SIZE = 4096 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 # === FUNCTIONS ============================================================== 176 # === FUNCTIONS ==============================================================
170 177
@@ -527,12 +534,14 @@ def process_file(container, filename, data, output_dir=None): @@ -527,12 +534,14 @@ def process_file(container, filename, data, output_dir=None):
527 print ('File: %r' % filename) 534 print ('File: %r' % filename)
528 index = 1 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 # look for ole files inside file (e.g. unzip docx) 542 # look for ole files inside file (e.g. unzip docx)
531 - flag_no_ole = False  
532 - flag_stream_error = False  
533 for ole in find_ole(filename, data): 543 for ole in find_ole(filename, data):
534 if ole is None: # no ole file found 544 if ole is None: # no ole file found
535 - flag_no_ole = True  
536 continue 545 continue
537 546
538 for path_parts in ole.listdir(): 547 for path_parts in ole.listdir():
@@ -542,13 +551,14 @@ def process_file(container, filename, data, output_dir=None): @@ -542,13 +551,14 @@ def process_file(container, filename, data, output_dir=None):
542 stream = None 551 stream = None
543 try: 552 try:
544 stream = ole.openstream(path_parts) 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 print ('Parsing OLE Package') 556 print ('Parsing OLE Package')
547 opkg = OleNativeStream(stream) 557 opkg = OleNativeStream(stream)
548 # leave stream open until dumping is finished 558 # leave stream open until dumping is finished
549 except Exception: 559 except Exception:
550 log.warning('*** Not an OLE 1.0 Object ({0})'.format(exc)) 560 log.warning('*** Not an OLE 1.0 Object ({0})'.format(exc))
551 - flag_stream_error = True 561 + err_stream = True
552 if stream is not None: 562 if stream is not None:
553 stream.close() 563 stream.close()
554 continue 564 continue
@@ -583,18 +593,22 @@ def process_file(container, filename, data, output_dir=None): @@ -583,18 +593,22 @@ def process_file(container, filename, data, output_dir=None):
583 break 593 break
584 next_size = min(DUMP_CHUNK_SIZE, 594 next_size = min(DUMP_CHUNK_SIZE,
585 opkg.actual_size - n_dumped) 595 opkg.actual_size - n_dumped)
  596 + did_dump = True
586 except Exception as exc: 597 except Exception as exc:
587 log.warning('error dumping to {0} ({1})' 598 log.warning('error dumping to {0} ({1})'
588 .format(fname, exc)) 599 .format(fname, exc))
  600 + err_dumping = True
589 finally: 601 finally:
590 stream.close() 602 stream.close()
591 603
592 index += 1 604 index += 1
  605 + return err_stream, err_dumping, did_dump
593 606
594 607
595 #=== MAIN ================================================================= 608 #=== MAIN =================================================================
596 609
597 def main(): 610 def main():
  611 + """ main function, called when running this as script """
598 # print banner with version 612 # print banner with version
599 print ('oleobj %s - http://decalage.info/oletools' % __version__) 613 print ('oleobj %s - http://decalage.info/oletools' % __version__)
600 print ('THIS IS WORK IN PROGRESS - Check updates regularly!') 614 print ('THIS IS WORK IN PROGRESS - Check updates regularly!')
@@ -640,9 +654,12 @@ def main(): @@ -640,9 +654,12 @@ def main():
640 654
641 # Print help if no arguments are passed 655 # Print help if no arguments are passed
642 if len(args) == 0: 656 if len(args) == 0:
643 - print (__doc__)  
644 parser.print_help() 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 # Setup logging to the console: 664 # Setup logging to the console:
648 # here we use stdout instead of stderr by default, so that the output 665 # here we use stdout instead of stderr by default, so that the output
@@ -652,14 +669,32 @@ def main(): @@ -652,14 +669,32 @@ def main():
652 # enable logging in the modules: 669 # enable logging in the modules:
653 log.setLevel(logging.NOTSET) 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 for container, filename, data in xglob.iter_files(args, recursive=options.recursive, 677 for container, filename, data in xglob.iter_files(args, recursive=options.recursive,
657 zip_password=options.zip_password, zip_fname=options.zip_fname): 678 zip_password=options.zip_password, zip_fname=options.zip_fname):
658 # ignore directory names stored in zip files: 679 # ignore directory names stored in zip files:
659 if container and filename.endswith('/'): 680 if container and filename.endswith('/'):
660 continue 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())