Commit 1665aeea56540af9923a5dae996327d9d0ae8b5a
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()) | ... | ... |