Skip to content

Commit d730787

Browse files
Merge pull request #304 from TeamMsgExtractor/next-release
Version 0.37.0
2 parents f112d14 + db67840 commit d730787

File tree

6 files changed

+52
-38
lines changed

6 files changed

+52
-38
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
**v0.37.0**
2+
* [[TeamMsgExtractor #303](https://github.com/TeamMsgExtractor/msg-extractor/issues/303)] Renamed internal variable that wasn't changed when the other instances or it were renamed.
3+
* [[TeamMsgExtractor #302](https://github.com/TeamMsgExtractor/msg-extractor/issues/302)] Fixed properties missing (a fatal MSG error) raising an unclear exception. It now uses `InvalidFileFormatError`.
4+
* Updated `README` to contain documentation on command line option added in previous version.
5+
* Removed `MSGFile.mainProperties` after deprecating it in v0.36.0.
6+
* Added new `Properties` `Intelligence` type: `ERROR`. This type is used when a properties instance is created but has something wrong with it that is not necessarily fatal. Currently the only thing that will cause it is the properties stream being 0 bytes.
7+
18
**v0.36.5**
29
* Documented and exposed to the command line the ability to save the headers to it's own file when saving the msg file. Thanks to martin-mueller-cemas on GitHub for fixing this (and telling me I can switch the branch a pull request points to). I don't know when I added the code to allow the user to do it, but somehow it was never documented and never exposed to the command line.
310

README.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ refer to the usage information provided from the program's help dialog:
8989
--allow-fallback Tells the program to fallback to a different save type if the selected one is not possible.
9090
--skip-body-not-found Skips saving the body if the body cannot be found, rather than throwing an error.
9191
--zip ZIP Path to use for saving to a zip file.
92+
--save-header Store the header in a separate file.
9293
--attachments-only Specify to only save attachments from an msg file.
9394
--no-folders When used with --attachments-only, stores everything in the location specified by --out. Incompatible with --out-name.
9495
--skip-embedded Skips all embedded MSG files when saving attachments.
@@ -230,8 +231,8 @@ your access to the newest major version of extract-msg.
230231
.. |License: GPL v3| image:: https://img.shields.io/badge/License-GPLv3-blue.svg
231232
:target: LICENSE.txt
232233

233-
.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.36.5-blue.svg
234-
:target: https://pypi.org/project/extract-msg/0.36.5/
234+
.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.37.0 -blue.svg
235+
:target: https://pypi.org/project/extract-msg/0.37.0/
235236

236237
.. |PyPI2| image:: https://img.shields.io/badge/python-3.6+-brightgreen.svg
237238
:target: https://www.python.org/downloads/release/python-367/

extract_msg/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2828

2929
__author__ = 'Destiny Peterson & Matthew Walker'
30-
__date__ = '2022-11-05'
31-
__version__ = '0.36.5'
30+
__date__ = '2022-11-17'
31+
__version__ = '0.37.0'
3232

3333
import logging
3434

extract_msg/enums.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@ class Importance(enum.Enum):
10771077

10781078

10791079
class Intelligence(enum.Enum):
1080+
ERROR = -1
10801081
DUMB = 0
10811082
SMART = 1
10821083

extract_msg/msg.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ def __init__(self, path, **kwargs):
5353
specified by the msg file. Do not report encoding errors caused by
5454
this.
5555
56-
:raises InvalidFileFormatError: If the file is not an OleFile.
56+
:raises InvalidFileFormatError: If the file is not an OleFile or could
57+
not be parsed as an MSG file.
5758
:raises IOError: If there is an issue opening the MSG file.
5859
:raises NameError: If the encoding provided is not supported.
5960
:raises TypeError: If the prefix is not a supported type.
@@ -705,12 +706,6 @@ def kwargs(self) -> dict:
705706
"""
706707
return self.__kwargs
707708

708-
@property
709-
def mainProperties(self) -> Properties:
710-
warn('`MSGFile.mainProperties` is deprecated and will soon be ' + \
711-
'removed. Please use `MSGFile.props` instead.', DeprecationWarning)
712-
return self.props
713-
714709
@property
715710
def props(self) -> Properties:
716711
"""
@@ -719,7 +714,12 @@ def props(self) -> Properties:
719714
try:
720715
return self._prop
721716
except AttributeError:
722-
self._prop = Properties(self._getStream('__properties_version1.0'),
717+
stream = self._getStream('__properties_version1.0')
718+
if not stream:
719+
# Raise the exception from None so we don't get all the "during
720+
# the handling of the above exception" stuff.
721+
raise InvalidFileFormatError('File does not contain a properties stream.') from None
722+
self._prop = Properties(stream,
723723
PropertiesType.MESSAGE if self.prefix == '' else PropertiesType.MESSAGE_EMBED)
724724
return self._prop
725725

extract_msg/properties.py

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ class Properties:
2020
Parser for msg properties files.
2121
"""
2222

23-
def __init__(self, data : bytes, type : Optional[PropertiesType] = None, skip : Optional[int] = None):
23+
def __init__(self, data : bytes, _type : Optional[PropertiesType] = None, skip : Optional[int] = None):
24+
if not isinstance(data, bytes):
25+
raise TypeError(':param data: MUST be bytes.')
2426
self.__rawData = data
2527
self.__pos = 0
2628
self.__len = len(data)
@@ -29,31 +31,34 @@ def __init__(self, data : bytes, type : Optional[PropertiesType] = None, skip :
2931
self.__nrid = None
3032
self.__ac = None
3133
self.__rc = None
32-
if type is not None:
33-
type = PropertiesType(type)
34-
self.__intel = Intelligence.SMART
35-
if type == PropertiesType.MESSAGE:
36-
skip = 32
37-
self.__nrid, self.__naid, self.__rc, self.__ac = constants.ST1.unpack(self.__rawData[:24])
38-
elif type == PropertiesType.MESSAGE_EMBED:
39-
skip = 24
40-
self.__nrid, self.__naid, self.__rc, self.__ac = constants.ST1.unpack(self.__rawData[:24])
41-
else:
42-
skip = 8
34+
# Handle an empty properties stream.
35+
if self.__len == 0:
36+
self.__intel = Intelligence.ERROR
37+
skip = 0
38+
elif _type is not None:
39+
_type = PropertiesType(_type)
40+
self.__intel = Intelligence.SMART
41+
if _type == PropertiesType.MESSAGE:
42+
skip = 32
43+
self.__nrid, self.__naid, self.__rc, self.__ac = constants.ST1.unpack(self.__rawData[:24])
44+
elif _type == PropertiesType.MESSAGE_EMBED:
45+
skip = 24
46+
self.__nrid, self.__naid, self.__rc, self.__ac = constants.ST1.unpack(self.__rawData[:24])
47+
else:
48+
skip = 8
4349
else:
4450
self.__intel = Intelligence.DUMB
4551
if skip is None:
46-
# This section of the skip handling is not very good.
47-
# While it does work, it is likely to create extra
48-
# properties that are created from the properties file's
49-
# header data. While that won't actually mess anything
50-
# up, it is far from ideal. Basically, this is the dumb
51-
# skip length calculation. Preferably, we want the type
52-
# to have been specified so all of the additional fields
53-
# will have been filled out
54-
skip = self.__len % 16
55-
if skip == 0:
56-
skip = 32
52+
# This section of the skip handling is not very good. While it
53+
# does work, it is likely to create extra properties that are
54+
# created from the properties file's header data. While that
55+
# won't actually mess anything up, it is far from ideal.
56+
# Basically, this is the dumb skip length calculation.
57+
# Preferably, we want the type to have been specified so all of
58+
# the additional fields will have been filled out.
59+
#
60+
# If the skip would end up at 0, set it to 32.
61+
skip = (self.__len % 16) or 32
5762
streams = divide(self.__rawData[skip:], 16)
5863
for st in streams:
5964
if len(st) == 16:
@@ -91,11 +96,11 @@ def get(self, name, default = None) -> Optional[Union[PropBase, Any]]:
9196
except KeyError:
9297
# DEBUG
9398
logger.debug('KeyError exception.')
94-
logger.debug(properHex(self.__stream))
99+
logger.debug(properHex(self.__rawData))
95100
logger.debug(self.__props)
96101
return default
97102

98-
def has_key(self, key):
103+
def has_key(self, key) -> bool:
99104
"""
100105
Checks if :param key: is a key in the properties dictionary.
101106
"""
@@ -107,7 +112,7 @@ def items(self):
107112
def keys(self):
108113
return self.__props.keys()
109114

110-
def pprintKeys(self):
115+
def pprintKeys(self) -> None:
111116
"""
112117
Uses the pprint function on a sorted list of keys.
113118
"""

0 commit comments

Comments
 (0)