Commit c16ca78e7fa836ca8cbd3f23e6896edb1763f1c9

Authored by decalage2
1 parent b460e8e2

oletimes: added optparse and xglob to process multiple files and zips (issue #141)

Showing 1 changed file with 63 additions and 28 deletions
oletools/oletimes.py
@@ -49,19 +49,19 @@ http://www.decalage.info/python/oletools @@ -49,19 +49,19 @@ http://www.decalage.info/python/oletools
49 # 2016-07-20 v0.50 SL: - added Python 3 support 49 # 2016-07-20 v0.50 SL: - added Python 3 support
50 # 2016-09-05 PL: - added main entry point for setup.py 50 # 2016-09-05 PL: - added main entry point for setup.py
51 # 2017-05-03 v0.51 PL: - fixed absolute imports (issue #141) 51 # 2017-05-03 v0.51 PL: - fixed absolute imports (issue #141)
  52 +# 2017-05-04 PL: - added optparse and xglob
52 53
53 __version__ = '0.51dev7' 54 __version__ = '0.51dev7'
54 55
55 #------------------------------------------------------------------------------ 56 #------------------------------------------------------------------------------
56 # TODO: 57 # TODO:
57 -# + optparse  
58 # + nicer output: table with fixed columns, datetime, etc 58 # + nicer output: table with fixed columns, datetime, etc
59 # + CSV output 59 # + CSV output
60 # + option to only show available timestamps (by default?) 60 # + option to only show available timestamps (by default?)
61 61
62 #=== IMPORTS ================================================================= 62 #=== IMPORTS =================================================================
63 63
64 -import sys, os, datetime 64 +import sys, os, optparse
65 65
66 # IMPORTANT: it should be possible to run oletools directly as scripts 66 # IMPORTANT: it should be possible to run oletools directly as scripts
67 # in any directory without installing them with pip or setup.py. 67 # in any directory without installing them with pip or setup.py.
@@ -76,47 +76,82 @@ if not _parent_dir in sys.path: @@ -76,47 +76,82 @@ if not _parent_dir in sys.path:
76 sys.path.insert(0, _parent_dir) 76 sys.path.insert(0, _parent_dir)
77 77
78 from oletools.thirdparty import olefile 78 from oletools.thirdparty import olefile
  79 +from oletools.thirdparty import xglob
79 from oletools.thirdparty.prettytable import prettytable 80 from oletools.thirdparty.prettytable import prettytable
80 81
81 82
82 -# === MAIN =================================================================== 83 +# === FUNCTIONS ==============================================================
83 84
84 -def main():  
85 - # print banner with version  
86 - print('oletimes %s - http://decalage.info/python/oletools' % __version__) 85 +def dt2str(dt):
  86 + """
  87 + Convert a datetime object to a string for display, without microseconds
87 88
88 - try:  
89 - ole = olefile.OleFileIO(sys.argv[1])  
90 - except IndexError:  
91 - sys.exit(__doc__) 89 + :param dt: datetime.datetime object, or None
  90 + :return: str, or None
  91 + """
  92 + if dt is None:
  93 + return None
  94 + dt = dt.replace(microsecond=0)
  95 + return str(dt)
92 96
93 - def dt2str (dt):  
94 - """  
95 - Convert a datetime object to a string for display, without microseconds  
96 -  
97 - :param dt: datetime.datetime object, or None  
98 - :return: str, or None  
99 - """  
100 - if dt is None:  
101 - return None  
102 - dt = dt.replace(microsecond = 0)  
103 - return str(dt)  
104 97
  98 +def process_ole(ole):
105 t = prettytable.PrettyTable(['Stream/Storage name', 'Modification Time', 'Creation Time']) 99 t = prettytable.PrettyTable(['Stream/Storage name', 'Modification Time', 'Creation Time'])
106 t.align = 'l' 100 t.align = 'l'
107 t.max_width = 26 101 t.max_width = 26
108 - #t.border = False  
109 -  
110 - #print'- Root mtime=%s ctime=%s' % (ole.root.getmtime(), ole.root.getctime())  
111 t.add_row(('Root', dt2str(ole.root.getmtime()), dt2str(ole.root.getctime()))) 102 t.add_row(('Root', dt2str(ole.root.getmtime()), dt2str(ole.root.getctime())))
112 -  
113 for obj in ole.listdir(streams=True, storages=True): 103 for obj in ole.listdir(streams=True, storages=True):
114 - #print '- %s: mtime=%s ctime=%s' % (repr('/'.join(obj)), ole.getmtime(obj), ole.getctime(obj))  
115 t.add_row((repr('/'.join(obj)), dt2str(ole.getmtime(obj)), dt2str(ole.getctime(obj)))) 104 t.add_row((repr('/'.join(obj)), dt2str(ole.getmtime(obj)), dt2str(ole.getctime(obj))))
116 -  
117 print(t) 105 print(t)
118 106
119 - ole.close() 107 +
  108 +# === MAIN ===================================================================
  109 +
  110 +def main():
  111 + # print banner with version
  112 + print('oletimes %s - http://decalage.info/python/oletools' % __version__)
  113 + print ('THIS IS WORK IN PROGRESS - Check updates regularly!')
  114 + print ('Please report any issue at https://github.com/decalage2/oletools/issues')
  115 + print ('')
  116 +
  117 + usage = 'usage: oletimes [options] <filename> [filename2 ...]'
  118 + parser = optparse.OptionParser(usage=usage)
  119 + parser.add_option("-r", action="store_true", dest="recursive",
  120 + help='find files recursively in subdirectories.')
  121 + parser.add_option("-z", "--zip", dest='zip_password', type='str', default=None,
  122 + help='if the file is a zip archive, open all files from it, using the provided password (requires Python 2.6+)')
  123 + parser.add_option("-f", "--zipfname", dest='zip_fname', type='str', default='*',
  124 + help='if the file is a zip archive, file(s) to be opened within the zip. Wildcards * and ? are supported. (default:*)')
  125 +
  126 + # TODO: add logfile option
  127 + # parser.add_option('-l', '--loglevel', dest="loglevel", action="store", default=DEFAULT_LOG_LEVEL,
  128 + # help="logging level debug/info/warning/error/critical (default=%default)")
  129 +
  130 + (options, args) = parser.parse_args()
  131 +
  132 + # Print help if no arguments are passed
  133 + if len(args) == 0:
  134 + print(__doc__)
  135 + parser.print_help()
  136 + sys.exit()
  137 +
  138 + for container, filename, data in xglob.iter_files(args, recursive=options.recursive,
  139 + zip_password=options.zip_password, zip_fname=options.zip_fname):
  140 + # TODO: handle xglob errors
  141 + # ignore directory names stored in zip files:
  142 + if container and filename.endswith('/'):
  143 + continue
  144 + full_name = '%s in %s' % (filename, container) if container else filename
  145 + print("=" * 79)
  146 + print('FILE: %s\n' % full_name)
  147 + if data is not None:
  148 + # data extracted from zip file
  149 + ole = olefile.OleFileIO(data)
  150 + else:
  151 + # normal filename
  152 + ole = olefile.OleFileIO(filename)
  153 + process_ole(ole)
  154 + ole.close()
120 155
121 if __name__ == '__main__': 156 if __name__ == '__main__':
122 main() 157 main()