├── .gitignore ├── requirements.txt ├── setup.py ├── LICENSE ├── README.md └── outlookmsgfile.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | compoundfiles 2 | compressed-rtf 3 | rtfparse # Python 3.9+ only 4 | html2text -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import setuptools 3 | 4 | setup_path = os.path.dirname(os.path.realpath(__file__)) 5 | 6 | install_requires = [ 7 | 'compoundfiles', 8 | 'compressed_rtf', 9 | 'rtfparse', 10 | 'html2text', 11 | ] 12 | 13 | with open(f"{setup_path}/README.md", "r") as fh: 14 | long_description = fh.read() 15 | 16 | setuptools.setup( 17 | name='convert-outlook-msg-file', 18 | version='0.2.0', 19 | description='Parse Microsoft Outlook MSG files', 20 | author='Joshua Tauberer', 21 | author_email='jt@occams.info', 22 | url='https://github.com/JoshData/convert-outlook-msg-file', 23 | py_modules=['outlookmsgfile'], 24 | install_requires=install_requires, 25 | long_description=long_description, 26 | long_description_content_type="text/markdown", 27 | python_requires='>=3.9', 28 | classifiers=[ 29 | "Programming Language :: Python :: 3", 30 | "License :: OSI Approved :: MIT License", 31 | "Operating System :: OS Independent", 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Joshua Tauberer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Convert Outlook .msg Files to .eml (MIME format) 2 | ================================================ 3 | 4 | This repository contains a Python 3.9+ module for 5 | reading Microsoft Outlook .msg files and converting 6 | them to .eml format, which is the standard MIME 7 | format for email messages. 8 | 9 | This project uses 10 | [compoundfiles](https://pypi.org/project/compoundfiles/) 11 | to navigate the .msg file structure, 12 | [compressed-rtf](https://pypi.org/project/compressed-rtf/) 13 | and [rtfparse](https://pypi.org/project/rtfparse/) 14 | to unpack HTML message bodies, and 15 | [html2text](https://pypi.org/project/html2text/) to 16 | back-fill plain text message bodies when only an HTML body 17 | is present. 18 | 19 | Install the dependencies with: 20 | 21 | pip install -r requirements.txt 22 | 23 | (You may need to create and activate a Python virtual environment first.) 24 | 25 | Then either convert a single file by piping: 26 | 27 | python outlookmsgfile.py < message.msg > message.eml 28 | 29 | Or convert a set of files: 30 | 31 | python outlookmsgfile.py *.msg 32 | 33 | When passing filenames as command-line arguments, a new file with `.eml` 34 | appended to the filename is written out with the message in MIME format. 35 | 36 | To use it in your application 37 | 38 | ```python 39 | import outlookmsgfile 40 | eml = outlookmsgfile.load('my_email_sample.msg') 41 | ``` 42 | 43 | The ``load()`` function returns an [EmailMessage](https://docs.python.org/3/library/email.message.html#email.message.EmailMessage) instance. -------------------------------------------------------------------------------- /outlookmsgfile.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # This module converts a Microsoft Outlook .msg file into 4 | # a MIME message that can be loaded by most email programs 5 | # or inspected in a text editor. 6 | # 7 | # This script relies on the Python package compoundfiles 8 | # for reading the .msg container format. 9 | # 10 | # Referencecs: 11 | # 12 | # https://msdn.microsoft.com/en-us/library/cc463912.aspx 13 | # https://msdn.microsoft.com/en-us/library/cc463900(v=exchg.80).aspx 14 | # https://msdn.microsoft.com/en-us/library/ee157583(v=exchg.80).aspx 15 | # https://blogs.msdn.microsoft.com/openspecification/2009/11/06/msg-file-format-part-1/ 16 | 17 | import re 18 | import logging 19 | import os 20 | import sys 21 | import io 22 | 23 | from functools import reduce 24 | 25 | import email.message, email.parser, email.policy 26 | from email.utils import parsedate_to_datetime, formatdate, formataddr 27 | 28 | import compoundfiles 29 | from rtfparse.parser import Rtf_Parser 30 | from rtfparse.renderers.html_decapsulator import HTML_Decapsulator 31 | import html2text 32 | 33 | logger = logging.getLogger(__name__) 34 | 35 | FALLBACK_ENCODING = 'cp1252' 36 | 37 | # MAIN FUNCTIONS 38 | 39 | 40 | def load(filename_or_stream): 41 | with compoundfiles.CompoundFileReader(filename_or_stream) as doc: 42 | doc.rtf_attachments = 0 43 | return load_message_stream(doc.root, True, doc) 44 | 45 | 46 | def load_message_stream(entry, is_top_level, doc): 47 | # Load stream data. 48 | props = parse_properties(entry['__properties_version1.0'], is_top_level, entry, doc) 49 | 50 | # Construct the MIME message.... 51 | msg = email.message.EmailMessage() 52 | 53 | # Add the raw headers, if known. 54 | if 'TRANSPORT_MESSAGE_HEADERS' in props: 55 | # Get the string holding all of the headers. 56 | headers = props['TRANSPORT_MESSAGE_HEADERS'] 57 | if isinstance(headers, bytes): 58 | headers = headers.decode("utf-8") 59 | 60 | # Remove content-type header because the body we can get this 61 | # way is just the plain-text portion of the email and whatever 62 | # Content-Type header was in the original is not valid for 63 | # reconstructing it this way. 64 | headers = re.sub(r"Content-Type: .*(\n\s.*)*\n", "", headers, flags=re.I) 65 | 66 | # Parse them. 67 | headers = email.parser.HeaderParser(policy=email.policy.default)\ 68 | .parsestr(headers) 69 | 70 | # Copy them into the message object. 71 | for header, value in headers.items(): 72 | msg[header] = value 73 | 74 | else: 75 | # Construct common headers from metadata. 76 | 77 | if 'MESSAGE_DELIVERY_TIME' in props: 78 | msg['Date'] = formatdate(props['MESSAGE_DELIVERY_TIME'].timestamp()) 79 | del props['MESSAGE_DELIVERY_TIME'] 80 | 81 | if 'SENDER_NAME' in props: 82 | if 'SENT_REPRESENTING_NAME' in props: 83 | if props['SENT_REPRESENTING_NAME']: 84 | if props['SENDER_NAME'] != props['SENT_REPRESENTING_NAME']: 85 | props['SENDER_NAME'] += " (" + props['SENT_REPRESENTING_NAME'] + ")" 86 | del props['SENT_REPRESENTING_NAME'] 87 | if props['SENDER_NAME']: 88 | msg['From'] = formataddr((props['SENDER_NAME'], "")) 89 | del props['SENDER_NAME'] 90 | 91 | if 'DISPLAY_TO' in props: 92 | if props['DISPLAY_TO']: 93 | msg['To'] = props['DISPLAY_TO'].split(";") 94 | del props['DISPLAY_TO'] 95 | 96 | if 'DISPLAY_CC' in props: 97 | if props['DISPLAY_CC']: 98 | msg['CC'] = props['DISPLAY_CC'].split(";") 99 | del props['DISPLAY_CC'] 100 | 101 | if 'DISPLAY_BCC' in props: 102 | if props['DISPLAY_BCC']: 103 | msg['BCC'] = props['DISPLAY_BCC'].split(";") 104 | del props['DISPLAY_BCC'] 105 | 106 | if 'SUBJECT' in props: 107 | if props['SUBJECT']: 108 | msg['Subject'] = props['SUBJECT'] 109 | del props['SUBJECT'] 110 | 111 | # Add a plain text body from the BODY field. 112 | has_body = False 113 | if 'BODY' in props: 114 | body = props['BODY'] 115 | if isinstance(body, str): 116 | msg.set_content(body, cte='quoted-printable') 117 | else: 118 | msg.set_content(body, maintype="text", subtype="plain", cte='8bit') 119 | has_body = True 120 | 121 | # Add a HTML body from the RTF_COMPRESSED field. 122 | if 'RTF_COMPRESSED' in props: 123 | # Decompress the value to Rich Text Format. 124 | import compressed_rtf 125 | rtf = props['RTF_COMPRESSED'] 126 | rtf = compressed_rtf.decompress(rtf) 127 | 128 | # Try rtfparse to de-encapsulate HTML stored in a rich 129 | # text container. 130 | try: 131 | rtf_blob = io.BytesIO(rtf) 132 | parsed = Rtf_Parser(rtf_file=rtf_blob).parse_file() 133 | html_stream = io.StringIO() 134 | HTML_Decapsulator().render(parsed, html_stream) 135 | html_body = html_stream.getvalue() 136 | 137 | if not has_body: 138 | # Try to convert that to plain/text if possible. 139 | text_body = html2text.html2text(html_body) 140 | msg.set_content(text_body, subtype="text", cte='quoted-printable') 141 | has_body = True 142 | 143 | if not has_body: 144 | msg.set_content(html_body, subtype="html", cte='quoted-printable') 145 | has_body = True 146 | else: 147 | msg.add_alternative(html_body, subtype="html", cte='quoted-printable') 148 | 149 | # If that fails, just attach the RTF file to the message. 150 | except: 151 | doc.rtf_attachments += 1 152 | fn = "messagebody_{}.rtf".format(doc.rtf_attachments) 153 | 154 | if not has_body: 155 | msg.set_content( 156 | "".format(fn), 157 | cte='quoted-printable') 158 | has_body = True 159 | 160 | # Add RTF file as an attachment. 161 | msg.add_attachment( 162 | rtf, 163 | maintype="text", subtype="rtf", 164 | filename=fn) 165 | 166 | if not has_body: 167 | msg.set_content("", cte='quoted-printable') 168 | 169 | # # Copy over string values of remaining properties as headers 170 | # # so we don't lose any information. 171 | # for k, v in props.items(): 172 | # if k == 'RTF_COMPRESSED': continue # not interested, save output 173 | # msg[k] = str(v) 174 | 175 | # Add attachments. 176 | for stream in entry: 177 | if stream.name.startswith("__attach_version1.0_#"): 178 | try: 179 | process_attachment(msg, stream, doc) 180 | except KeyError as e: 181 | logger.error("Error processing attachment {} not found".format(str(e))) 182 | continue 183 | 184 | return msg 185 | 186 | 187 | def process_attachment(msg, entry, doc): 188 | # Load attachment stream. 189 | props = parse_properties(entry['__properties_version1.0'], False, entry, doc) 190 | 191 | # The attachment content... 192 | blob = props['ATTACH_DATA_BIN'] 193 | 194 | # Get the filename and MIME type of the attachment. 195 | filename = props.get("ATTACH_LONG_FILENAME") or props.get("ATTACH_FILENAME") or props.get("DISPLAY_NAME") 196 | if isinstance(filename, bytes): filename = filename.decode("utf8") 197 | 198 | mime_type = props.get('ATTACH_MIME_TAG', 'application/octet-stream') 199 | if isinstance(mime_type, bytes): mime_type = mime_type.decode("utf8") 200 | 201 | try: 202 | filename = os.path.basename(filename) 203 | except (TypeError, ValueError) as e: 204 | logger.warning("Warning in processing attachment filename: {}".format(str(e))) 205 | filename = 'attachment' 206 | 207 | # Python 3.6. 208 | if isinstance(blob, str): 209 | msg.add_attachment( 210 | blob, 211 | filename=filename) 212 | elif isinstance(blob, bytes): 213 | msg.add_attachment( 214 | blob, 215 | maintype=mime_type.split("/", 1)[0], subtype=mime_type.split("/", 1)[-1], 216 | filename=filename) 217 | else: # a Message instance 218 | msg.add_attachment( 219 | blob, 220 | filename=filename) 221 | 222 | def parse_properties(properties, is_top_level, container, doc): 223 | # Read a properties stream and return a Python dictionary 224 | # of the fields and values, using human-readable field names 225 | # in the mapping at the top of this module. 226 | 227 | # Load stream content. 228 | with doc.open(properties) as stream: 229 | stream = stream.read() 230 | 231 | # Skip header. 232 | i = (32 if is_top_level else 24) 233 | 234 | # Read 16-byte entries. 235 | raw_properties = { } 236 | while i < len(stream): 237 | # Read the entry. 238 | property_type = stream[i+0:i+2] 239 | property_tag = stream[i+2:i+4] 240 | flags = stream[i+4:i+8] 241 | value = stream[i+8:i+16] 242 | i += 16 243 | 244 | # Turn the byte strings into numbers and look up the property type. 245 | property_type = property_type[0] + (property_type[1]<<8) 246 | property_tag = property_tag[0] + (property_tag[1]<<8) 247 | if property_tag not in property_tags: continue # should not happen 248 | tag_name, _ = property_tags[property_tag] 249 | tag_type = property_types.get(property_type) 250 | 251 | # Fixed Length Properties. 252 | if isinstance(tag_type, FixedLengthValueLoader): 253 | # The value comes from the stream above. 254 | pass 255 | 256 | # Variable Length Properties. 257 | elif isinstance(tag_type, VariableLengthValueLoader): 258 | value_length = stream[i+8:i+12] # not used 259 | 260 | # Look up the stream in the document that holds the value. 261 | streamname = "__substg1.0_{0:0{1}X}{2:0{3}X}".format(property_tag,4, property_type,4) 262 | try: 263 | with doc.open(container[streamname]) as innerstream: 264 | value = innerstream.read() 265 | except: 266 | # Stream isn't present! 267 | logger.error("stream missing {}".format(streamname)) 268 | continue 269 | 270 | elif isinstance(tag_type, EMBEDDED_MESSAGE): 271 | # Look up the stream in the document that holds the attachment. 272 | streamname = "__substg1.0_{0:0{1}X}{2:0{3}X}".format(property_tag,4, property_type,4) 273 | try: 274 | value = container[streamname] 275 | except: 276 | # Stream isn't present! 277 | logger.error("stream missing {}".format(streamname)) 278 | continue 279 | 280 | else: 281 | # unrecognized type 282 | logger.error("unhandled property type {}".format(hex(property_type))) 283 | continue 284 | 285 | raw_properties[tag_name] = (tag_type, value) 286 | 287 | # Decode all FixedLengthValueLoader properties so we have codepage 288 | # properties. 289 | properties = { } 290 | for tag_name, (tag_type, value) in raw_properties.items(): 291 | if not isinstance(tag_type, FixedLengthValueLoader): continue 292 | try: 293 | properties[tag_name] = tag_type.load(value) 294 | except Exception as e: 295 | logger.error("Error while reading stream: {}".format(str(e))) 296 | 297 | # String8 strings use code page information stored in other 298 | # properties, which may not be present. Find the Python 299 | # encoding to use. 300 | 301 | # The encoding of the "BODY" (and HTML body) properties. 302 | body_encoding = None 303 | if "PR_INTERNET_CPID" in properties and properties['PR_INTERNET_CPID'] in code_pages: 304 | body_encoding = code_pages[properties['PR_INTERNET_CPID']] 305 | 306 | # The encoding of "string properties of the message object". 307 | properties_encoding = None 308 | if "PR_MESSAGE_CODEPAGE" in properties and properties['PR_MESSAGE_CODEPAGE'] in code_pages: 309 | properties_encoding = code_pages[properties['PR_MESSAGE_CODEPAGE']] 310 | 311 | # Decode all of the remaining properties. 312 | for tag_name, (tag_type, value) in raw_properties.items(): 313 | if isinstance(tag_type, FixedLengthValueLoader): continue # already done, above 314 | 315 | # The codepage properties may be wrong. Fall back to 316 | # the other property if present. 317 | encodings = [body_encoding, properties_encoding] if tag_name == "BODY" \ 318 | else [properties_encoding, body_encoding] 319 | 320 | try: 321 | properties[tag_name] = tag_type.load(value, encodings=encodings, doc=doc) 322 | except KeyError as e: 323 | logger.error("Error while reading stream: {} not found".format(str(e))) 324 | except Exception as e: 325 | logger.error("Error while reading stream: {}".format(str(e))) 326 | 327 | return properties 328 | 329 | 330 | # PROPERTY VALUE LOADERS 331 | 332 | class FixedLengthValueLoader(object): 333 | pass 334 | 335 | class NULL(FixedLengthValueLoader): 336 | @staticmethod 337 | def load(value): 338 | # value is an eight-byte long bytestring with unused content. 339 | return None 340 | 341 | class BOOLEAN(FixedLengthValueLoader): 342 | @staticmethod 343 | def load(value): 344 | # value is an eight-byte long bytestring holding a two-byte integer. 345 | return value[0] == 1 346 | 347 | class INTEGER16(FixedLengthValueLoader): 348 | @staticmethod 349 | def load(value): 350 | # value is an eight-byte long bytestring holding a two-byte integer. 351 | return reduce(lambda a, b : (a<<8)+b, reversed(value[0:2])) 352 | 353 | class INTEGER32(FixedLengthValueLoader): 354 | @staticmethod 355 | def load(value): 356 | # value is an eight-byte long bytestring holding a four-byte integer. 357 | return reduce(lambda a, b : (a<<8)+b, reversed(value[0:4])) 358 | 359 | class INTEGER64(FixedLengthValueLoader): 360 | @staticmethod 361 | def load(value): 362 | # value is an eight-byte long bytestring holding an eight-byte integer. 363 | return reduce(lambda a, b : (a<<8)+b, reversed(value)) 364 | 365 | class INTTIME(FixedLengthValueLoader): 366 | @staticmethod 367 | def load(value): 368 | # value is an eight-byte long bytestring encoding the integer number of 369 | # 100-nanosecond intervals since January 1, 1601. 370 | from datetime import datetime, timedelta 371 | value = reduce(lambda a, b : (a<<8)+b, reversed(value)) # bytestring to integer 372 | try: 373 | value = datetime(1601, 1, 1) + timedelta(seconds=value/10000000) 374 | except OverflowError: 375 | value = None 376 | 377 | return value 378 | 379 | # TODO: The other fixed-length data types: 380 | # "FLOAT", "DOUBLE", "CURRENCY", "APPTIME", "ERROR" 381 | 382 | class VariableLengthValueLoader(object): 383 | pass 384 | 385 | class BINARY(VariableLengthValueLoader): 386 | @staticmethod 387 | def load(value, **kwargs): 388 | # value is a bytestring. Just return it. 389 | return value 390 | 391 | class STRING8(VariableLengthValueLoader): 392 | @staticmethod 393 | def load(value, encodings, **kwargs): 394 | # Some strings are C-style "null terminated" 395 | value = value.removesuffix(b'\0') 396 | 397 | # "value" is of type "bytes" and "encodings" is a list of Python 398 | # codecs to try. If all fail, try the fallback codec with 399 | # character replacement so that this never fails. 400 | 401 | for encoding in encodings: 402 | try: 403 | return value.decode(encoding=encoding, errors='strict') 404 | except: 405 | # Try the next one. 406 | pass 407 | return value.decode(encoding=FALLBACK_ENCODING, errors='replace') 408 | 409 | class UNICODE(VariableLengthValueLoader): 410 | @staticmethod 411 | def load(value, **kwargs): 412 | # value is a bytestring encoded in UTF-16. 413 | return value.decode("utf16") 414 | 415 | # TODO: The other variable-length tag types are "CLSID", "OBJECT". 416 | 417 | class EMBEDDED_MESSAGE(object): 418 | @staticmethod 419 | def load(entry, doc, **kwargs): 420 | return load_message_stream(entry, False, doc) 421 | 422 | 423 | # CONSTANTS 424 | 425 | # These constants are defined by the Microsoft Outlook file format 426 | # and identify the data types and data fields in the .msg file. 427 | 428 | # from mapidefs.h via https://github.com/inverse-inc/openchange.old/blob/master/libmapi/mapidefs.h 429 | property_types = { 430 | 0x1: NULL(), 431 | 0x2: INTEGER16(), 432 | 0x3: INTEGER32(), 433 | 0x4: "FLOAT", 434 | 0x5: "DOUBLE", 435 | 0x6: "CURRENCY", 436 | 0x7: "APPTIME", 437 | 0xa: "ERROR", 438 | 0xb: BOOLEAN(), 439 | 0xd: EMBEDDED_MESSAGE(), 440 | 0x14: INTEGER64(), 441 | 0x1e: STRING8(), 442 | 0x1f: UNICODE(), 443 | 0x40: INTTIME(), 444 | 0x48: "CLSID", 445 | 0xFB: "SVREID", 446 | 0xFD: "SRESTRICT", 447 | 0xFE: "ACTIONS", 448 | 0x102: BINARY(), 449 | } 450 | 451 | # from mapitags.h via https://github.com/mvz/email-outlook-message-perl/blob/master/mapitags.h 452 | property_tags = { 453 | 0x01: ('ACKNOWLEDGEMENT_MODE', 'I4'), 454 | 0x02: ('ALTERNATE_RECIPIENT_ALLOWED', 'BOOLEAN'), 455 | 0x03: ('AUTHORIZING_USERS', 'BINARY'), 456 | # Comment on an automatically forwarded message 457 | 0x04: ('AUTO_FORWARD_COMMENT', 'STRING'), 458 | # Whether a message has been automatically forwarded 459 | 0x05: ('AUTO_FORWARDED', 'BOOLEAN'), 460 | 0x06: ('CONTENT_CONFIDENTIALITY_ALGORITHM_ID', 'BINARY'), 461 | 0x07: ('CONTENT_CORRELATOR', 'BINARY'), 462 | 0x08: ('CONTENT_IDENTIFIER', 'STRING'), 463 | # MIME content length 464 | 0x09: ('CONTENT_LENGTH', 'I4'), 465 | 0x0A: ('CONTENT_RETURN_REQUESTED', 'BOOLEAN'), 466 | 0x0B: ('CONVERSATION_KEY', 'BINARY'), 467 | 0x0C: ('CONVERSION_EITS', 'BINARY'), 468 | 0x0D: ('CONVERSION_WITH_LOSS_PROHIBITED', 'BOOLEAN'), 469 | 0x0E: ('CONVERTED_EITS', 'BINARY'), 470 | # Time to deliver for delayed delivery messages 471 | 0x0F: ('DEFERRED_DELIVERY_TIME', 'SYSTIME'), 472 | 0x10: ('DELIVER_TIME', 'SYSTIME'), 473 | # Reason a message was discarded 474 | 0x11: ('DISCARD_REASON', 'I4'), 475 | 0x12: ('DISCLOSURE_OF_RECIPIENTS', 'BOOLEAN'), 476 | 0x13: ('DL_EXPANSION_HISTORY', 'BINARY'), 477 | 0x14: ('DL_EXPANSION_PROHIBITED', 'BOOLEAN'), 478 | 0x15: ('EXPIRY_TIME', 'SYSTIME'), 479 | 0x16: ('IMPLICIT_CONVERSION_PROHIBITED', 'BOOLEAN'), 480 | # Message importance 481 | 0x17: ('IMPORTANCE', 'I4'), 482 | 0x18: ('IPM_ID', 'BINARY'), 483 | 0x19: ('LATEST_DELIVERY_TIME', 'SYSTIME'), 484 | 0x1A: ('MESSAGE_CLASS', 'STRING'), 485 | 0x1B: ('MESSAGE_DELIVERY_ID', 'BINARY'), 486 | 0x1E: ('MESSAGE_SECURITY_LABEL', 'BINARY'), 487 | 0x1F: ('OBSOLETED_IPMS', 'BINARY'), 488 | # Person a message was originally for 489 | 0x20: ('ORIGINALLY_INTENDED_RECIPIENT_NAME', 'BINARY'), 490 | 0x21: ('ORIGINAL_EITS', 'BINARY'), 491 | 0x22: ('ORIGINATOR_CERTIFICATE', 'BINARY'), 492 | 0x23: ('ORIGINATOR_DELIVERY_REPORT_REQUESTED', 'BOOLEAN'), 493 | # Address of the message sender 494 | 0x24: ('ORIGINATOR_RETURN_ADDRESS', 'BINARY'), 495 | 0x25: ('PARENT_KEY', 'BINARY'), 496 | 0x26: ('PRIORITY', 'I4'), 497 | 0x27: ('ORIGIN_CHECK', 'BINARY'), 498 | 0x28: ('PROOF_OF_SUBMISSION_REQUESTED', 'BOOLEAN'), 499 | # Whether a read receipt is desired 500 | 0x29: ('READ_RECEIPT_REQUESTED', 'BOOLEAN'), 501 | # Time a message was received 502 | 0x2A: ('RECEIPT_TIME', 'SYSTIME'), 503 | 0x2B: ('RECIPIENT_REASSIGNMENT_PROHIBITED', 'BOOLEAN'), 504 | 0x2C: ('REDIRECTION_HISTORY', 'BINARY'), 505 | 0x2D: ('RELATED_IPMS', 'BINARY'), 506 | # Sensitivity of the original message 507 | 0x2E: ('ORIGINAL_SENSITIVITY', 'I4'), 508 | 0x2F: ('LANGUAGES', 'STRING'), 509 | 0x30: ('REPLY_TIME', 'SYSTIME'), 510 | 0x31: ('REPORT_TAG', 'BINARY'), 511 | 0x32: ('REPORT_TIME', 'SYSTIME'), 512 | 0x33: ('RETURNED_IPM', 'BOOLEAN'), 513 | 0x34: ('SECURITY', 'I4'), 514 | 0x35: ('INCOMPLETE_COPY', 'BOOLEAN'), 515 | 0x36: ('SENSITIVITY', 'I4'), 516 | # The message subject 517 | 0x37: ('SUBJECT', 'STRING'), 518 | 0x38: ('SUBJECT_IPM', 'BINARY'), 519 | 0x39: ('CLIENT_SUBMIT_TIME', 'SYSTIME'), 520 | 0x3A: ('REPORT_NAME', 'STRING'), 521 | 0x3B: ('SENT_REPRESENTING_SEARCH_KEY', 'BINARY'), 522 | 0x3C: ('X400_CONTENT_TYPE', 'BINARY'), 523 | 0x3D: ('SUBJECT_PREFIX', 'STRING'), 524 | 0x3E: ('NON_RECEIPT_REASON', 'I4'), 525 | 0x3F: ('RECEIVED_BY_ENTRYID', 'BINARY'), 526 | # Received by: entry 527 | 0x40: ('RECEIVED_BY_NAME', 'STRING'), 528 | 0x41: ('SENT_REPRESENTING_ENTRYID', 'BINARY'), 529 | 0x42: ('SENT_REPRESENTING_NAME', 'STRING'), 530 | 0x43: ('RCVD_REPRESENTING_ENTRYID', 'BINARY'), 531 | 0x44: ('RCVD_REPRESENTING_NAME', 'STRING'), 532 | 0x45: ('REPORT_ENTRYID', 'BINARY'), 533 | 0x46: ('READ_RECEIPT_ENTRYID', 'BINARY'), 534 | 0x47: ('MESSAGE_SUBMISSION_ID', 'BINARY'), 535 | 0x48: ('PROVIDER_SUBMIT_TIME', 'SYSTIME'), 536 | # Subject of the original message 537 | 0x49: ('ORIGINAL_SUBJECT', 'STRING'), 538 | 0x4A: ('DISC_VAL', 'BOOLEAN'), 539 | 0x4B: ('ORIG_MESSAGE_CLASS', 'STRING'), 540 | 0x4C: ('ORIGINAL_AUTHOR_ENTRYID', 'BINARY'), 541 | # Author of the original message 542 | 0x4D: ('ORIGINAL_AUTHOR_NAME', 'STRING'), 543 | # Time the original message was submitted 544 | 0x4E: ('ORIGINAL_SUBMIT_TIME', 'SYSTIME'), 545 | 0x4F: ('REPLY_RECIPIENT_ENTRIES', 'BINARY'), 546 | 0x50: ('REPLY_RECIPIENT_NAMES', 'STRING'), 547 | 0x51: ('RECEIVED_BY_SEARCH_KEY', 'BINARY'), 548 | 0x52: ('RCVD_REPRESENTING_SEARCH_KEY', 'BINARY'), 549 | 0x53: ('READ_RECEIPT_SEARCH_KEY', 'BINARY'), 550 | 0x54: ('REPORT_SEARCH_KEY', 'BINARY'), 551 | 0x55: ('ORIGINAL_DELIVERY_TIME', 'SYSTIME'), 552 | 0x56: ('ORIGINAL_AUTHOR_SEARCH_KEY', 'BINARY'), 553 | 0x57: ('MESSAGE_TO_ME', 'BOOLEAN'), 554 | 0x58: ('MESSAGE_CC_ME', 'BOOLEAN'), 555 | 0x59: ('MESSAGE_RECIP_ME', 'BOOLEAN'), 556 | # Sender of the original message 557 | 0x5A: ('ORIGINAL_SENDER_NAME', 'STRING'), 558 | 0x5B: ('ORIGINAL_SENDER_ENTRYID', 'BINARY'), 559 | 0x5C: ('ORIGINAL_SENDER_SEARCH_KEY', 'BINARY'), 560 | 0x5D: ('ORIGINAL_SENT_REPRESENTING_NAME', 'STRING'), 561 | 0x5E: ('ORIGINAL_SENT_REPRESENTING_ENTRYID', 'BINARY'), 562 | 0x5F: ('ORIGINAL_SENT_REPRESENTING_SEARCH_KEY', 'BINARY'), 563 | 0x60: ('START_DATE', 'SYSTIME'), 564 | 0x61: ('END_DATE', 'SYSTIME'), 565 | 0x62: ('OWNER_APPT_ID', 'I4'), 566 | # Whether a response to the message is desired 567 | 0x63: ('RESPONSE_REQUESTED', 'BOOLEAN'), 568 | 0x64: ('SENT_REPRESENTING_ADDRTYPE', 'STRING'), 569 | 0x65: ('SENT_REPRESENTING_EMAIL_ADDRESS', 'STRING'), 570 | 0x66: ('ORIGINAL_SENDER_ADDRTYPE', 'STRING'), 571 | # Email of the original message sender 572 | 0x67: ('ORIGINAL_SENDER_EMAIL_ADDRESS', 'STRING'), 573 | 0x68: ('ORIGINAL_SENT_REPRESENTING_ADDRTYPE', 'STRING'), 574 | 0x69: ('ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS', 'STRING'), 575 | 0x70: ('CONVERSATION_TOPIC', 'STRING'), 576 | 0x71: ('CONVERSATION_INDEX', 'BINARY'), 577 | 0x72: ('ORIGINAL_DISPLAY_BCC', 'STRING'), 578 | 0x73: ('ORIGINAL_DISPLAY_CC', 'STRING'), 579 | 0x74: ('ORIGINAL_DISPLAY_TO', 'STRING'), 580 | 0x75: ('RECEIVED_BY_ADDRTYPE', 'STRING'), 581 | 0x76: ('RECEIVED_BY_EMAIL_ADDRESS', 'STRING'), 582 | 0x77: ('RCVD_REPRESENTING_ADDRTYPE', 'STRING'), 583 | 0x78: ('RCVD_REPRESENTING_EMAIL_ADDRESS', 'STRING'), 584 | 0x79: ('ORIGINAL_AUTHOR_ADDRTYPE', 'STRING'), 585 | 0x7A: ('ORIGINAL_AUTHOR_EMAIL_ADDRESS', 'STRING'), 586 | 0x7B: ('ORIGINALLY_INTENDED_RECIP_ADDRTYPE', 'STRING'), 587 | 0x7C: ('ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS', 'STRING'), 588 | 0x7D: ('TRANSPORT_MESSAGE_HEADERS', 'STRING'), 589 | 0x7E: ('DELEGATION', 'BINARY'), 590 | 0x7F: ('TNEF_CORRELATION_KEY', 'BINARY'), 591 | 0x1000: ('BODY', 'STRING'), 592 | 0x1001: ('REPORT_TEXT', 'STRING'), 593 | 0x1002: ('ORIGINATOR_AND_DL_EXPANSION_HISTORY', 'BINARY'), 594 | 0x1003: ('REPORTING_DL_NAME', 'BINARY'), 595 | 0x1004: ('REPORTING_MTA_CERTIFICATE', 'BINARY'), 596 | 0x1006: ('RTF_SYNC_BODY_CRC', 'I4'), 597 | 0x1007: ('RTF_SYNC_BODY_COUNT', 'I4'), 598 | 0x1008: ('RTF_SYNC_BODY_TAG', 'STRING'), 599 | 0x1009: ('RTF_COMPRESSED', 'BINARY'), 600 | 0x1010: ('RTF_SYNC_PREFIX_COUNT', 'I4'), 601 | 0x1011: ('RTF_SYNC_TRAILING_COUNT', 'I4'), 602 | 0x1012: ('ORIGINALLY_INTENDED_RECIP_ENTRYID', 'BINARY'), 603 | 0x0C00: ('CONTENT_INTEGRITY_CHECK', 'BINARY'), 604 | 0x0C01: ('EXPLICIT_CONVERSION', 'I4'), 605 | 0x0C02: ('IPM_RETURN_REQUESTED', 'BOOLEAN'), 606 | 0x0C03: ('MESSAGE_TOKEN', 'BINARY'), 607 | 0x0C04: ('NDR_REASON_CODE', 'I4'), 608 | 0x0C05: ('NDR_DIAG_CODE', 'I4'), 609 | 0x0C06: ('NON_RECEIPT_NOTIFICATION_REQUESTED', 'BOOLEAN'), 610 | 0x0C07: ('DELIVERY_POINT', 'I4'), 611 | 0x0C08: ('ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED', 'BOOLEAN'), 612 | 0x0C09: ('ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT', 'BINARY'), 613 | 0x0C0A: ('PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY', 'BOOLEAN'), 614 | 0x0C0B: ('PHYSICAL_DELIVERY_MODE', 'I4'), 615 | 0x0C0C: ('PHYSICAL_DELIVERY_REPORT_REQUEST', 'I4'), 616 | 0x0C0D: ('PHYSICAL_FORWARDING_ADDRESS', 'BINARY'), 617 | 0x0C0E: ('PHYSICAL_FORWARDING_ADDRESS_REQUESTED', 'BOOLEAN'), 618 | 0x0C0F: ('PHYSICAL_FORWARDING_PROHIBITED', 'BOOLEAN'), 619 | 0x0C10: ('PHYSICAL_RENDITION_ATTRIBUTES', 'BINARY'), 620 | 0x0C11: ('PROOF_OF_DELIVERY', 'BINARY'), 621 | 0x0C12: ('PROOF_OF_DELIVERY_REQUESTED', 'BOOLEAN'), 622 | 0x0C13: ('RECIPIENT_CERTIFICATE', 'BINARY'), 623 | 0x0C14: ('RECIPIENT_NUMBER_FOR_ADVICE', 'STRING'), 624 | 0x0C15: ('RECIPIENT_TYPE', 'I4'), 625 | 0x0C16: ('REGISTERED_MAIL_TYPE', 'I4'), 626 | 0x0C17: ('REPLY_REQUESTED', 'BOOLEAN'), 627 | 0x0C18: ('REQUESTED_DELIVERY_METHOD', 'I4'), 628 | 0x0C19: ('SENDER_ENTRYID', 'BINARY'), 629 | 0x0C1A: ('SENDER_NAME', 'STRING'), 630 | 0x0C1B: ('SUPPLEMENTARY_INFO', 'STRING'), 631 | 0x0C1C: ('TYPE_OF_MTS_USER', 'I4'), 632 | 0x0C1D: ('SENDER_SEARCH_KEY', 'BINARY'), 633 | 0x0C1E: ('SENDER_ADDRTYPE', 'STRING'), 634 | 0x0C1F: ('SENDER_EMAIL_ADDRESS', 'STRING'), 635 | 0x0E00: ('CURRENT_VERSION', 'I8'), 636 | 0x0E01: ('DELETE_AFTER_SUBMIT', 'BOOLEAN'), 637 | 0x0E02: ('DISPLAY_BCC', 'STRING'), 638 | 0x0E03: ('DISPLAY_CC', 'STRING'), 639 | 0x0E04: ('DISPLAY_TO', 'STRING'), 640 | 0x0E05: ('PARENT_DISPLAY', 'STRING'), 641 | 0x0E06: ('MESSAGE_DELIVERY_TIME', 'SYSTIME'), 642 | 0x0E07: ('MESSAGE_FLAGS', 'I4'), 643 | 0x0E08: ('MESSAGE_SIZE', 'I4'), 644 | 0x0E09: ('PARENT_ENTRYID', 'BINARY'), 645 | 0x0E0A: ('SENTMAIL_ENTRYID', 'BINARY'), 646 | 0x0E0C: ('CORRELATE', 'BOOLEAN'), 647 | 0x0E0D: ('CORRELATE_MTSID', 'BINARY'), 648 | 0x0E0E: ('DISCRETE_VALUES', 'BOOLEAN'), 649 | 0x0E0F: ('RESPONSIBILITY', 'BOOLEAN'), 650 | 0x0E10: ('SPOOLER_STATUS', 'I4'), 651 | 0x0E11: ('TRANSPORT_STATUS', 'I4'), 652 | 0x0E12: ('MESSAGE_RECIPIENTS', 'OBJECT'), 653 | 0x0E13: ('MESSAGE_ATTACHMENTS', 'OBJECT'), 654 | 0x0E14: ('SUBMIT_FLAGS', 'I4'), 655 | 0x0E15: ('RECIPIENT_STATUS', 'I4'), 656 | 0x0E16: ('TRANSPORT_KEY', 'I4'), 657 | 0x0E17: ('MSG_STATUS', 'I4'), 658 | 0x0E18: ('MESSAGE_DOWNLOAD_TIME', 'I4'), 659 | 0x0E19: ('CREATION_VERSION', 'I8'), 660 | 0x0E1A: ('MODIFY_VERSION', 'I8'), 661 | 0x0E1B: ('HASATTACH', 'BOOLEAN'), 662 | 0x0E1D: ('NORMALIZED_SUBJECT', 'STRING'), 663 | 0x0E1F: ('RTF_IN_SYNC', 'BOOLEAN'), 664 | 0x0E20: ('ATTACH_SIZE', 'I4'), 665 | 0x0E21: ('ATTACH_NUM', 'I4'), 666 | 0x0E22: ('PREPROCESS', 'BOOLEAN'), 667 | 0x0E25: ('ORIGINATING_MTA_CERTIFICATE', 'BINARY'), 668 | 0x0E26: ('PROOF_OF_SUBMISSION', 'BINARY'), 669 | # A unique identifier for editing the properties of a MAPI object 670 | 0x0FFF: ('ENTRYID', 'BINARY'), 671 | # The type of an object 672 | 0x0FFE: ('OBJECT_TYPE', 'I4'), 673 | 0x0FFD: ('ICON', 'BINARY'), 674 | 0x0FFC: ('MINI_ICON', 'BINARY'), 675 | 0x0FFB: ('STORE_ENTRYID', 'BINARY'), 676 | 0x0FFA: ('STORE_RECORD_KEY', 'BINARY'), 677 | # Binary identifer for an individual object 678 | 0x0FF9: ('RECORD_KEY', 'BINARY'), 679 | 0x0FF8: ('MAPPING_SIGNATURE', 'BINARY'), 680 | 0x0FF7: ('ACCESS_LEVEL', 'I4'), 681 | # The primary key of a column in a table 682 | 0x0FF6: ('INSTANCE_KEY', 'BINARY'), 683 | 0x0FF5: ('ROW_TYPE', 'I4'), 684 | 0x0FF4: ('ACCESS', 'I4'), 685 | 0x3000: ('ROWID', 'I4'), 686 | # The name to display for a given MAPI object 687 | 0x3001: ('DISPLAY_NAME', 'STRING'), 688 | 0x3002: ('ADDRTYPE', 'STRING'), 689 | # An email address 690 | 0x3003: ('EMAIL_ADDRESS', 'STRING'), 691 | # A comment field 692 | 0x3004: ('COMMENT', 'STRING'), 693 | 0x3005: ('DEPTH', 'I4'), 694 | # Provider-defined display name for a service provider 695 | 0x3006: ('PROVIDER_DISPLAY', 'STRING'), 696 | # The time an object was created 697 | 0x3007: ('CREATION_TIME', 'SYSTIME'), 698 | # The time an object was last modified 699 | 0x3008: ('LAST_MODIFICATION_TIME', 'SYSTIME'), 700 | # Flags describing a service provider, message service, or status object 701 | 0x3009: ('RESOURCE_FLAGS', 'I4'), 702 | # The name of a provider dll, minus any "32" suffix and ".dll" 703 | 0x300A: ('PROVIDER_DLL_NAME', 'STRING'), 704 | 0x300B: ('SEARCH_KEY', 'BINARY'), 705 | 0x300C: ('PROVIDER_UID', 'BINARY'), 706 | 0x300D: ('PROVIDER_ORDINAL', 'I4'), 707 | 0x3301: ('FORM_VERSION', 'STRING'), 708 | 0x3302: ('FORM_CLSID', 'CLSID'), 709 | 0x3303: ('FORM_CONTACT_NAME', 'STRING'), 710 | 0x3304: ('FORM_CATEGORY', 'STRING'), 711 | 0x3305: ('FORM_CATEGORY_SUB', 'STRING'), 712 | 0x3306: ('FORM_HOST_MAP', 'MV_LONG'), 713 | 0x3307: ('FORM_HIDDEN', 'BOOLEAN'), 714 | 0x3308: ('FORM_DESIGNER_NAME', 'STRING'), 715 | 0x3309: ('FORM_DESIGNER_GUID', 'CLSID'), 716 | 0x330A: ('FORM_MESSAGE_BEHAVIOR', 'I4'), 717 | # Is this row the default message store? 718 | 0x3400: ('DEFAULT_STORE', 'BOOLEAN'), 719 | 0x340D: ('STORE_SUPPORT_MASK', 'I4'), 720 | 0x340E: ('STORE_STATE', 'I4'), 721 | 0x3410: ('IPM_SUBTREE_SEARCH_KEY', 'BINARY'), 722 | 0x3411: ('IPM_OUTBOX_SEARCH_KEY', 'BINARY'), 723 | 0x3412: ('IPM_WASTEBASKET_SEARCH_KEY', 'BINARY'), 724 | 0x3413: ('IPM_SENTMAIL_SEARCH_KEY', 'BINARY'), 725 | # Provder-defined message store type 726 | 0x3414: ('MDB_PROVIDER', 'BINARY'), 727 | 0x3415: ('RECEIVE_FOLDER_SETTINGS', 'OBJECT'), 728 | 0x35DF: ('VALID_FOLDER_MASK', 'I4'), 729 | 0x35E0: ('IPM_SUBTREE_ENTRYID', 'BINARY'), 730 | 0x35E2: ('IPM_OUTBOX_ENTRYID', 'BINARY'), 731 | 0x35E3: ('IPM_WASTEBASKET_ENTRYID', 'BINARY'), 732 | 0x35E4: ('IPM_SENTMAIL_ENTRYID', 'BINARY'), 733 | 0x35E5: ('VIEWS_ENTRYID', 'BINARY'), 734 | 0x35E6: ('COMMON_VIEWS_ENTRYID', 'BINARY'), 735 | 0x35E7: ('FINDER_ENTRYID', 'BINARY'), 736 | 0x3600: ('CONTAINER_FLAGS', 'I4'), 737 | 0x3601: ('FOLDER_TYPE', 'I4'), 738 | 0x3602: ('CONTENT_COUNT', 'I4'), 739 | 0x3603: ('CONTENT_UNREAD', 'I4'), 740 | 0x3604: ('CREATE_TEMPLATES', 'OBJECT'), 741 | 0x3605: ('DETAILS_TABLE', 'OBJECT'), 742 | 0x3607: ('SEARCH', 'OBJECT'), 743 | 0x3609: ('SELECTABLE', 'BOOLEAN'), 744 | 0x360A: ('SUBFOLDERS', 'BOOLEAN'), 745 | 0x360B: ('STATUS', 'I4'), 746 | 0x360C: ('ANR', 'STRING'), 747 | 0x360D: ('CONTENTS_SORT_ORDER', 'MV_LONG'), 748 | 0x360E: ('CONTAINER_HIERARCHY', 'OBJECT'), 749 | 0x360F: ('CONTAINER_CONTENTS', 'OBJECT'), 750 | 0x3610: ('FOLDER_ASSOCIATED_CONTENTS', 'OBJECT'), 751 | 0x3611: ('DEF_CREATE_DL', 'BINARY'), 752 | 0x3612: ('DEF_CREATE_MAILUSER', 'BINARY'), 753 | 0x3613: ('CONTAINER_CLASS', 'STRING'), 754 | 0x3614: ('CONTAINER_MODIFY_VERSION', 'I8'), 755 | 0x3615: ('AB_PROVIDER_ID', 'BINARY'), 756 | 0x3616: ('DEFAULT_VIEW_ENTRYID', 'BINARY'), 757 | 0x3617: ('ASSOC_CONTENT_COUNT', 'I4'), 758 | 0x3700: ('ATTACHMENT_X400_PARAMETERS', 'BINARY'), 759 | 0x3701: ('ATTACH_DATA_OBJ', 'OBJECT'), 760 | 0x3701: ('ATTACH_DATA_BIN', 'BINARY'), 761 | 0x3702: ('ATTACH_ENCODING', 'BINARY'), 762 | 0x3703: ('ATTACH_EXTENSION', 'STRING'), 763 | 0x3704: ('ATTACH_FILENAME', 'STRING'), 764 | 0x3705: ('ATTACH_METHOD', 'I4'), 765 | 0x3707: ('ATTACH_LONG_FILENAME', 'STRING'), 766 | 0x3708: ('ATTACH_PATHNAME', 'STRING'), 767 | 0x370A: ('ATTACH_TAG', 'BINARY'), 768 | 0x370B: ('RENDERING_POSITION', 'I4'), 769 | 0x370C: ('ATTACH_TRANSPORT_NAME', 'STRING'), 770 | 0x370D: ('ATTACH_LONG_PATHNAME', 'STRING'), 771 | 0x370E: ('ATTACH_MIME_TAG', 'STRING'), 772 | 0x370F: ('ATTACH_ADDITIONAL_INFO', 'BINARY'), 773 | 0x3900: ('DISPLAY_TYPE', 'I4'), 774 | 0x3902: ('TEMPLATEID', 'BINARY'), 775 | 0x3904: ('PRIMARY_CAPABILITY', 'BINARY'), 776 | 0x39FF: ('7BIT_DISPLAY_NAME', 'STRING'), 777 | 0x3A00: ('ACCOUNT', 'STRING'), 778 | 0x3A01: ('ALTERNATE_RECIPIENT', 'BINARY'), 779 | 0x3A02: ('CALLBACK_TELEPHONE_NUMBER', 'STRING'), 780 | 0x3A03: ('CONVERSION_PROHIBITED', 'BOOLEAN'), 781 | 0x3A04: ('DISCLOSE_RECIPIENTS', 'BOOLEAN'), 782 | 0x3A05: ('GENERATION', 'STRING'), 783 | 0x3A06: ('GIVEN_NAME', 'STRING'), 784 | 0x3A07: ('GOVERNMENT_ID_NUMBER', 'STRING'), 785 | 0x3A08: ('BUSINESS_TELEPHONE_NUMBER', 'STRING'), 786 | 0x3A09: ('HOME_TELEPHONE_NUMBER', 'STRING'), 787 | 0x3A0A: ('INITIALS', 'STRING'), 788 | 0x3A0B: ('KEYWORD', 'STRING'), 789 | 0x3A0C: ('LANGUAGE', 'STRING'), 790 | 0x3A0D: ('LOCATION', 'STRING'), 791 | 0x3A0E: ('MAIL_PERMISSION', 'BOOLEAN'), 792 | 0x3A0F: ('MHS_COMMON_NAME', 'STRING'), 793 | 0x3A10: ('ORGANIZATIONAL_ID_NUMBER', 'STRING'), 794 | 0x3A11: ('SURNAME', 'STRING'), 795 | 0x3A12: ('ORIGINAL_ENTRYID', 'BINARY'), 796 | 0x3A13: ('ORIGINAL_DISPLAY_NAME', 'STRING'), 797 | 0x3A14: ('ORIGINAL_SEARCH_KEY', 'BINARY'), 798 | 0x3A15: ('POSTAL_ADDRESS', 'STRING'), 799 | 0x3A16: ('COMPANY_NAME', 'STRING'), 800 | 0x3A17: ('TITLE', 'STRING'), 801 | 0x3A18: ('DEPARTMENT_NAME', 'STRING'), 802 | 0x3A19: ('OFFICE_LOCATION', 'STRING'), 803 | 0x3A1A: ('PRIMARY_TELEPHONE_NUMBER', 'STRING'), 804 | 0x3A1B: ('BUSINESS2_TELEPHONE_NUMBER', 'STRING'), 805 | 0x3A1C: ('MOBILE_TELEPHONE_NUMBER', 'STRING'), 806 | 0x3A1D: ('RADIO_TELEPHONE_NUMBER', 'STRING'), 807 | 0x3A1E: ('CAR_TELEPHONE_NUMBER', 'STRING'), 808 | 0x3A1F: ('OTHER_TELEPHONE_NUMBER', 'STRING'), 809 | 0x3A20: ('TRANSMITABLE_DISPLAY_NAME', 'STRING'), 810 | 0x3A21: ('PAGER_TELEPHONE_NUMBER', 'STRING'), 811 | 0x3A22: ('USER_CERTIFICATE', 'BINARY'), 812 | 0x3A23: ('PRIMARY_FAX_NUMBER', 'STRING'), 813 | 0x3A24: ('BUSINESS_FAX_NUMBER', 'STRING'), 814 | 0x3A25: ('HOME_FAX_NUMBER', 'STRING'), 815 | 0x3A26: ('COUNTRY', 'STRING'), 816 | 0x3A27: ('LOCALITY', 'STRING'), 817 | 0x3A28: ('STATE_OR_PROVINCE', 'STRING'), 818 | 0x3A29: ('STREET_ADDRESS', 'STRING'), 819 | 0x3A2A: ('POSTAL_CODE', 'STRING'), 820 | 0x3A2B: ('POST_OFFICE_BOX', 'STRING'), 821 | 0x3A2C: ('TELEX_NUMBER', 'STRING'), 822 | 0x3A2D: ('ISDN_NUMBER', 'STRING'), 823 | 0x3A2E: ('ASSISTANT_TELEPHONE_NUMBER', 'STRING'), 824 | 0x3A2F: ('HOME2_TELEPHONE_NUMBER', 'STRING'), 825 | 0x3A30: ('ASSISTANT', 'STRING'), 826 | 0x3A40: ('SEND_RICH_INFO', 'BOOLEAN'), 827 | 0x3A41: ('WEDDING_ANNIVERSARY', 'SYSTIME'), 828 | 0x3A42: ('BIRTHDAY', 'SYSTIME'), 829 | 0x3A43: ('HOBBIES', 'STRING'), 830 | 0x3A44: ('MIDDLE_NAME', 'STRING'), 831 | 0x3A45: ('DISPLAY_NAME_PREFIX', 'STRING'), 832 | 0x3A46: ('PROFESSION', 'STRING'), 833 | 0x3A47: ('PREFERRED_BY_NAME', 'STRING'), 834 | 0x3A48: ('SPOUSE_NAME', 'STRING'), 835 | 0x3A49: ('COMPUTER_NETWORK_NAME', 'STRING'), 836 | 0x3A4A: ('CUSTOMER_ID', 'STRING'), 837 | 0x3A4B: ('TTYTDD_PHONE_NUMBER', 'STRING'), 838 | 0x3A4C: ('FTP_SITE', 'STRING'), 839 | 0x3A4D: ('GENDER', 'I2'), 840 | 0x3A4E: ('MANAGER_NAME', 'STRING'), 841 | 0x3A4F: ('NICKNAME', 'STRING'), 842 | 0x3A50: ('PERSONAL_HOME_PAGE', 'STRING'), 843 | 0x3A51: ('BUSINESS_HOME_PAGE', 'STRING'), 844 | 0x3A52: ('CONTACT_VERSION', 'CLSID'), 845 | 0x3A53: ('CONTACT_ENTRYIDS', 'MV_BINARY'), 846 | 0x3A54: ('CONTACT_ADDRTYPES', 'MV_STRING'), 847 | 0x3A55: ('CONTACT_DEFAULT_ADDRESS_INDEX', 'I4'), 848 | 0x3A56: ('CONTACT_EMAIL_ADDRESSES', 'MV_STRING'), 849 | 0x3A57: ('COMPANY_MAIN_PHONE_NUMBER', 'STRING'), 850 | 0x3A58: ('CHILDRENS_NAMES', 'MV_STRING'), 851 | 0x3A59: ('HOME_ADDRESS_CITY', 'STRING'), 852 | 0x3A5A: ('HOME_ADDRESS_COUNTRY', 'STRING'), 853 | 0x3A5B: ('HOME_ADDRESS_POSTAL_CODE', 'STRING'), 854 | 0x3A5C: ('HOME_ADDRESS_STATE_OR_PROVINCE', 'STRING'), 855 | 0x3A5D: ('HOME_ADDRESS_STREET', 'STRING'), 856 | 0x3A5E: ('HOME_ADDRESS_POST_OFFICE_BOX', 'STRING'), 857 | 0x3A5F: ('OTHER_ADDRESS_CITY', 'STRING'), 858 | 0x3A60: ('OTHER_ADDRESS_COUNTRY', 'STRING'), 859 | 0x3A61: ('OTHER_ADDRESS_POSTAL_CODE', 'STRING'), 860 | 0x3A62: ('OTHER_ADDRESS_STATE_OR_PROVINCE', 'STRING'), 861 | 0x3A63: ('OTHER_ADDRESS_STREET', 'STRING'), 862 | 0x3A64: ('OTHER_ADDRESS_POST_OFFICE_BOX', 'STRING'), 863 | 0x3D00: ('STORE_PROVIDERS', 'BINARY'), 864 | 0x3D01: ('AB_PROVIDERS', 'BINARY'), 865 | 0x3D02: ('TRANSPORT_PROVIDERS', 'BINARY'), 866 | 0x3D04: ('DEFAULT_PROFILE', 'BOOLEAN'), 867 | 0x3D05: ('AB_SEARCH_PATH', 'MV_BINARY'), 868 | 0x3D06: ('AB_DEFAULT_DIR', 'BINARY'), 869 | 0x3D07: ('AB_DEFAULT_PAB', 'BINARY'), 870 | 0x3D09: ('SERVICE_NAME', 'STRING'), 871 | 0x3D0A: ('SERVICE_DLL_NAME', 'STRING'), 872 | 0x3D0B: ('SERVICE_ENTRY_NAME', 'STRING'), 873 | 0x3D0C: ('SERVICE_UID', 'BINARY'), 874 | 0x3D0D: ('SERVICE_EXTRA_UIDS', 'BINARY'), 875 | 0x3D0E: ('SERVICES', 'BINARY'), 876 | 0x3D0F: ('SERVICE_SUPPORT_FILES', 'MV_STRING'), 877 | 0x3D10: ('SERVICE_DELETE_FILES', 'MV_STRING'), 878 | 0x3D11: ('AB_SEARCH_PATH_UPDATE', 'BINARY'), 879 | 0x3D12: ('PROFILE_NAME', 'STRING'), 880 | 0x3E00: ('IDENTITY_DISPLAY', 'STRING'), 881 | 0x3E01: ('IDENTITY_ENTRYID', 'BINARY'), 882 | 0x3E02: ('RESOURCE_METHODS', 'I4'), 883 | # Service provider type 884 | 0x3E03: ('RESOURCE_TYPE', 'I4'), 885 | 0x3E04: ('STATUS_CODE', 'I4'), 886 | 0x3E05: ('IDENTITY_SEARCH_KEY', 'BINARY'), 887 | 0x3E06: ('OWN_STORE_ENTRYID', 'BINARY'), 888 | 0x3E07: ('RESOURCE_PATH', 'STRING'), 889 | 0x3E08: ('STATUS_STRING', 'STRING'), 890 | 0x3E09: ('X400_DEFERRED_DELIVERY_CANCEL', 'BOOLEAN'), 891 | 0x3E0A: ('HEADER_FOLDER_ENTRYID', 'BINARY'), 892 | 0x3E0B: ('REMOTE_PROGRESS', 'I4'), 893 | 0x3E0C: ('REMOTE_PROGRESS_TEXT', 'STRING'), 894 | 0x3E0D: ('REMOTE_VALIDATE_OK', 'BOOLEAN'), 895 | 0x3F00: ('CONTROL_FLAGS', 'I4'), 896 | 0x3F01: ('CONTROL_STRUCTURE', 'BINARY'), 897 | 0x3F02: ('CONTROL_TYPE', 'I4'), 898 | 0x3F03: ('DELTAX', 'I4'), 899 | 0x3F04: ('DELTAY', 'I4'), 900 | 0x3F05: ('XPOS', 'I4'), 901 | 0x3F06: ('YPOS', 'I4'), 902 | 0x3F07: ('CONTROL_ID', 'BINARY'), 903 | 0x3F08: ('INITIAL_DETAILS_PANE', 'I4'), 904 | 0x3FDE: ('PR_INTERNET_CPID', 'I4'), 905 | 0x3FFD: ('PR_MESSAGE_CODEPAGE', 'I4'), 906 | } 907 | 908 | code_pages = { 909 | # Microsoft code page id: python codec name 910 | 437: "cp437", 911 | 850: "cp850", 912 | 852: "cp852", 913 | 936: "gb2312", 914 | 1250: "cp1250", 915 | 1251: "cp1251", 916 | 1252: "cp1252", 917 | 1253: "cp1253", 918 | 1254: "cp1254", 919 | 1255: "cp1255", 920 | 1256: "cp1256", 921 | 1257: "cp1257", 922 | 1258: "cp1258", 923 | 20127: "ascii", 924 | 20866: "koi8-r", 925 | 21866: "koi8-u", 926 | 28591: "iso8859_1", 927 | 28592: "iso8859_2", 928 | 28593: "iso8859_3", 929 | 28594: "iso8859_4", 930 | 28595: "iso8859_5", 931 | 28596: "iso8859_6", 932 | 28597: "iso8859_7", 933 | 28598: "iso8859_8", 934 | 28599: "iso8859_9", 935 | 28603: "iso8859_13", 936 | 28605: "iso8859_15", 937 | 65001: "utf-8", 938 | } 939 | 940 | # COMMAND-LINE ENTRY POINT 941 | 942 | 943 | if __name__ == "__main__": 944 | # If no command-line arguments are given, convert the .msg 945 | # file on STDIN to .eml format on STDOUT. 946 | if len(sys.argv) <= 1: 947 | print(load(sys.stdin), file=sys.stdout) 948 | 949 | # Otherwise, for each file mentioned on the command-line, 950 | # convert it and save it to a file with ".eml" appended 951 | # to the name. 952 | else: 953 | for fn in sys.argv[1:]: 954 | print(fn + "...") 955 | msg = load(fn) 956 | with open(fn + ".eml", "wb") as f: 957 | f.write(msg.as_bytes()) 958 | --------------------------------------------------------------------------------