from __future__ import generators
# Dump every property we can find for a MAPI item
import pythoncom
import os, sys
import tempfile
from win32com.mapi import mapi, mapiutil
from win32com.mapi.mapitags import *
import win32clipboard
import mapi_driver
try:
TBL_ALL_COLUMNS = mapi.TBL_ALL_COLUMNS
except AttributeError: # missing in early versions
TBL_ALL_COLUMNS = 1
PR_USERFIELDS = 0x36E30102 # PROP_TAG(PT_BINARY, 0x36e3)
def GetPropTagName(obj, prop_tag):
hr, tags, array = obj.GetNamesFromIDs( (prop_tag,) )
if type(array[0][1])==type(u''):
name = array[0][1]
else:
name = mapiutil.GetPropTagName(prop_tag)
return name
# Also in new versions of mapituil
def GetAllProperties(obj, make_pretty = True):
tags = obj.GetPropList(0)
hr, data = obj.GetProps(tags)
ret = []
for tag, val in data:
if make_pretty:
name = GetPropTagName(obj, tag)
else:
name = tag
ret.append((name, tag, val))
return ret
def GetLargeProperty(item, prop_tag):
prop_tag = PROP_TAG(PT_BINARY, PROP_ID(prop_tag))
stream = item.OpenProperty(prop_tag,
pythoncom.IID_IStream,
0, 0)
chunks = []
while 1:
chunk = stream.Read(4096)
if not chunk:
break
chunks.append(chunk)
return "".join(chunks)
def FormatPropertyValue(prop_tag, prop_val, item, shorten, get_large_props):
# Do some magic rtf conversion
if PROP_ID(prop_tag) == PROP_ID(PR_RTF_COMPRESSED):
rtf_stream = item.OpenProperty(PR_RTF_COMPRESSED,
pythoncom.IID_IStream, 0, 0)
html_stream = mapi.WrapCompressedRTFStream(rtf_stream, 0)
prop_val = mapi.RTFStreamToHTML(html_stream)
prop_tag = PROP_TAG(PT_STRING8, PR_RTF_COMPRESSED)
prop_repr = None
if PROP_TYPE(prop_tag)==PT_ERROR:
if get_large_props and \
prop_val in [mapi.MAPI_E_NOT_ENOUGH_MEMORY,
'MAPI_E_NOT_ENOUGH_MEMORY']:
# Use magic to get a large property.
prop_val = GetLargeProperty(item, prop_tag)
prop_repr = repr(prop_val)
else:
prop_val = prop_repr = mapiutil.GetScodeString(prop_val)
if prop_repr is None:
prop_repr = repr(prop_val)
if shorten:
prop_repr = prop_repr[:50]
return prop_repr
def DumpItemProps(item, shorten, get_large_props, stream=None):
all_props = GetAllProperties(item)
all_props.sort() # sort by first tuple item, which is name :)
for prop_name, prop_tag, prop_val in all_props:
# If we want 'short' variables, drop 'not found' props.
if shorten and PROP_TYPE(prop_tag)==PT_ERROR \
and prop_val == mapi.MAPI_E_NOT_FOUND:
continue
prop_repr = FormatPropertyValue(prop_tag, prop_val, item,
shorten, get_large_props)
print >> stream, "%-20s: %s" % (prop_name, prop_repr)
print >> stream, "-- end of item properties --"
def DumpProps(driver, mapi_folder, subject, include_attach, shorten,
get_large, stream=None):
hr, data = mapi_folder.GetProps( (PR_DISPLAY_NAME_A,), 0)
name = data[0][1]
for item in driver.GetItemsWithValue(mapi_folder, PR_SUBJECT_A, subject):
DumpItemProps(item, shorten, get_large, stream)
if include_attach:
print >> stream
table = item.GetAttachmentTable(0)
rows = mapi.HrQueryAllRows(table, (PR_ATTACH_NUM,), None, None, 0)
for row in rows:
attach_num = row[0][1]
print >> stream, \
"Dumping attachment (PR_ATTACH_NUM=%d)" % (attach_num,)
attach = item.OpenAttach(attach_num, None,
mapi.MAPI_DEFERRED_ERRORS)
DumpItemProps(attach, shorten, get_large, stream)
print >> stream
print >> stream
# Generic table dumper.
def DumpTable(driver, table, name_query_ob, shorten, large_props, stream=None):
cols = table.QueryColumns(TBL_ALL_COLUMNS)
table.SetColumns(cols, 0)
rows = mapi.HrQueryAllRows(table, cols, None, None, 0)
print >> stream, \
"Table has %d rows, each with %d columns" % (len(rows), len(cols))
for row in rows:
print >> stream, "-- new row --"
for col in row:
prop_tag, prop_val = col
# If we want 'short' variables, drop 'not found' props.
if shorten and PROP_TYPE(prop_tag)==PT_ERROR \
and prop_val == mapi.MAPI_E_NOT_FOUND:
continue
prop_name = GetPropTagName(name_query_ob, prop_tag)
prop_repr = FormatPropertyValue(prop_tag, prop_val, name_query_ob,
shorten, large_props)
print >> stream, "%-20s: %s" % (prop_name, prop_repr)
# This dumps the raw binary data of the property Outlook uses to store
# user defined fields.
def FindAndDumpTableUserProps(driver, table, folder, shorten,
get_large_props, stream=None):
restriction = (mapi.RES_PROPERTY,
(mapi.RELOP_EQ,
PR_MESSAGE_CLASS_A,
(PR_MESSAGE_CLASS_A, 'IPC.MS.REN.USERFIELDS')))
cols = (PR_USERFIELDS,)
table.SetColumns(cols, 0)
rows = mapi.HrQueryAllRows(table, cols, restriction, None, 0)
assert len(rows)<=1, "Only expecting 1 (or 0) rows"
tag, val = rows[0][0]
prop_name = GetPropTagName(folder, tag)
prop_repr = FormatPropertyValue(tag, val, folder,
shorten, get_large_props)
print >> stream, "%-20s: %s" % (prop_name, prop_repr)
def usage(driver, extra = None):
folder_doc = driver.GetFolderNameDoc()
if extra:
print extra
print
msg = """\
Usage: %s [options ...] subject of the message
Dumps all properties for all messages that match the subject. Subject
matching is substring and ignore-case.
-c - Write output to the clipboard, ready for pasting into an email
-f - Search for the message in the specified folder (default = Inbox)
-s - Shorten long property values.
-a - Include attachments
-l - Get the data for very large properties via a stream
-n - Show top-level folder names and exit
--dump-folder
Dump the properties of the specified folder.
--dump-folder-assoc-contents
Dump the 'associated contents' table of the specified folder.
--dump-folder-user-props
Find and dump the PR_USERFIELDS field for the specified table.
%s
Use the -n option to see all top-level folder names from all stores.""" \
% (os.path.basename(sys.argv[0]),folder_doc)
print msg
sys.exit(1)
def main():
driver = mapi_driver.MAPIDriver()
import getopt
try:
opts, args = getopt.getopt(sys.argv[1:], "caf:snl",
["dump-folder",
"dump-folder-assoc-contents",
"dump-folder-user-props",
])
except getopt.error, e:
usage(driver, e)
folder_name = ""
shorten = False
get_large_props = False
include_attach = False
write_clipboard = False
dump_folder = dump_folder_assoc_contents = dump_folder_user_props = False
for opt, opt_val in opts:
if opt == "-f":
folder_name = opt_val
elif opt == "-c":
write_clipboard = True
elif opt == "--dump-folder":
dump_folder = True
elif opt == "--dump-folder-assoc-contents":
dump_folder_assoc_contents = True
elif opt == "--dump-folder-user-props":
dump_folder_user_props = True
elif opt == "-s":
shorten = True
elif opt == "-a":
include_attach = True
elif opt == "-l":
get_large_props = True
elif opt == "-n":
driver.DumpTopLevelFolders()
sys.exit(1)
else:
usage(driver, "Unknown arg '%s'" % opt)
stream = None
if write_clipboard:
stream_name = tempfile.mktemp("spambayes")
stream = open(stream_name, "w")
if not folder_name:
folder_name = "Inbox" # Assume this exists!
subject = " ".join(args)
is_table_dump = dump_folder_assoc_contents or \
dump_folder or dump_folder_user_props
if is_table_dump and subject or not is_table_dump and not subject:
if is_table_dump:
extra = "You must not specify a subject with '-p'"
else:
extra = "You must specify a subject (unless you use '-p')"
usage(driver, extra)
try:
folder = driver.FindFolder(folder_name)
except ValueError, details:
print details
sys.exit(1)
if is_table_dump:
if dump_folder:
DumpItemProps(folder, shorten, get_large_props, stream)
if dump_folder_assoc_contents:
table = folder.GetContentsTable(mapi.MAPI_ASSOCIATED)
DumpTable(driver, table, folder, shorten, get_large_props, stream)
if dump_folder_user_props:
table = folder.GetContentsTable(mapi.MAPI_ASSOCIATED)
FindAndDumpTableUserProps(driver, table, folder,
shorten, get_large_props, stream)
else:
DumpProps(driver, folder, subject, include_attach,
shorten, get_large_props, stream)
if write_clipboard:
stream.close()
stream = open(stream_name, "r")
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(stream.read())
stream.close()
os.unlink(stream_name)
print "Output successfuly written to the Windows clipboard"
if __name__=='__main__':
main()
syntax highlighted by Code2HTML, v. 0.9.1