diff --git a/oletools/oletimes.py b/oletools/oletimes.py index 0a4a0a7..1807958 100644 --- a/oletools/oletimes.py +++ b/oletools/oletimes.py @@ -49,19 +49,19 @@ http://www.decalage.info/python/oletools # 2016-07-20 v0.50 SL: - added Python 3 support # 2016-09-05 PL: - added main entry point for setup.py # 2017-05-03 v0.51 PL: - fixed absolute imports (issue #141) +# 2017-05-04 PL: - added optparse and xglob __version__ = '0.51dev7' #------------------------------------------------------------------------------ # TODO: -# + optparse # + nicer output: table with fixed columns, datetime, etc # + CSV output # + option to only show available timestamps (by default?) #=== IMPORTS ================================================================= -import sys, os, datetime +import sys, os, optparse # IMPORTANT: it should be possible to run oletools directly as scripts # in any directory without installing them with pip or setup.py. @@ -76,47 +76,82 @@ if not _parent_dir in sys.path: sys.path.insert(0, _parent_dir) from oletools.thirdparty import olefile +from oletools.thirdparty import xglob from oletools.thirdparty.prettytable import prettytable -# === MAIN =================================================================== +# === FUNCTIONS ============================================================== -def main(): - # print banner with version - print('oletimes %s - http://decalage.info/python/oletools' % __version__) +def dt2str(dt): + """ + Convert a datetime object to a string for display, without microseconds - try: - ole = olefile.OleFileIO(sys.argv[1]) - except IndexError: - sys.exit(__doc__) + :param dt: datetime.datetime object, or None + :return: str, or None + """ + if dt is None: + return None + dt = dt.replace(microsecond=0) + return str(dt) - def dt2str (dt): - """ - Convert a datetime object to a string for display, without microseconds - - :param dt: datetime.datetime object, or None - :return: str, or None - """ - if dt is None: - return None - dt = dt.replace(microsecond = 0) - return str(dt) +def process_ole(ole): t = prettytable.PrettyTable(['Stream/Storage name', 'Modification Time', 'Creation Time']) t.align = 'l' t.max_width = 26 - #t.border = False - - #print'- Root mtime=%s ctime=%s' % (ole.root.getmtime(), ole.root.getctime()) t.add_row(('Root', dt2str(ole.root.getmtime()), dt2str(ole.root.getctime()))) - for obj in ole.listdir(streams=True, storages=True): - #print '- %s: mtime=%s ctime=%s' % (repr('/'.join(obj)), ole.getmtime(obj), ole.getctime(obj)) t.add_row((repr('/'.join(obj)), dt2str(ole.getmtime(obj)), dt2str(ole.getctime(obj)))) - print(t) - ole.close() + +# === MAIN =================================================================== + +def main(): + # print banner with version + print('oletimes %s - http://decalage.info/python/oletools' % __version__) + print ('THIS IS WORK IN PROGRESS - Check updates regularly!') + print ('Please report any issue at https://github.com/decalage2/oletools/issues') + print ('') + + usage = 'usage: oletimes [options] [filename2 ...]' + parser = optparse.OptionParser(usage=usage) + parser.add_option("-r", action="store_true", dest="recursive", + help='find files recursively in subdirectories.') + parser.add_option("-z", "--zip", dest='zip_password', type='str', default=None, + help='if the file is a zip archive, open all files from it, using the provided password (requires Python 2.6+)') + parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*', + help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)') + + # TODO: add logfile option + # parser.add_option('-l', '--loglevel', dest="loglevel", action="store", default=DEFAULT_LOG_LEVEL, + # help="logging level debug/info/warning/error/critical (default=%default)") + + (options, args) = parser.parse_args() + + # Print help if no arguments are passed + if len(args) == 0: + print(__doc__) + parser.print_help() + sys.exit() + + for container, filename, data in xglob.iter_files(args, recursive=options.recursive, + zip_password=options.zip_password, zip_fname=options.zip_fname): + # TODO: handle xglob errors + # ignore directory names stored in zip files: + if container and filename.endswith('/'): + continue + full_name = '%s in %s' % (filename, container) if container else filename + print("=" * 79) + print('FILE: %s\n' % full_name) + if data is not None: + # data extracted from zip file + ole = olefile.OleFileIO(data) + else: + # normal filename + ole = olefile.OleFileIO(filename) + process_ole(ole) + ole.close() if __name__ == '__main__': main()