Skip to content

Commit f9fae3d

Browse files
Merge pull request #468 from TeamMsgExtractor/next-release
Version 0.55.0
2 parents 68858f0 + e368059 commit f9fae3d

File tree

6 files changed

+29
-28
lines changed

6 files changed

+29
-28
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
**v0.55.0**
2+
* [[TeamMsgExtractor #465](https://github.com/TeamMsgExtractor/msg-extractor/issues/465)] Added missing `msg.close()` to `openMsg()`. If the MSG file was actually just a plain OLE file, it would be left open.
3+
* Adjusted the default value of `maxNameLength` for `MessageBase.save()` to 40 instead of 256.
4+
* Adjusted exception handling for `MessageBase.save()` to properly report the reason a folder fails to be created.
5+
* Simplified some of the code for `MessageBase.save()`.
6+
* Fixed some typing information.
7+
18
**v0.54.1**
29
* [[TeamMsgExtractor #462](https://github.com/TeamMsgExtractor/msg-extractor/issues/462)] Fix potential issue where child MSG might have incompatible encoding to parent MSG when trying to grab a stream from the parent.
310
* Added code to attempt to significantly improve RTF deencapsulation times. This tries to strip away unneeded data before passing it to `RTFDE`. This shows improvements on all files that take more than one second. Currently, this actually fixes some files previously outputting wrong from `RTFDE` when deencapsulating the HTML body, specifically around non breaking spaces sometimes not transferring over.

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ your access to the newest major version of extract-msg.
260260
.. |License: GPL v3| image:: https://img.shields.io/badge/License-GPLv3-blue.svg
261261
:target: LICENSE.txt
262262

263-
.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.54.1-blue.svg
264-
:target: https://pypi.org/project/extract-msg/0.54.1/
263+
.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.55.0-blue.svg
264+
:target: https://pypi.org/project/extract-msg/0.55.0/
265265

266266
.. |PyPI2| image:: https://img.shields.io/badge/python-3.8+-brightgreen.svg
267267
:target: https://www.python.org/downloads/release/python-3810/

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__ = '2025-04-10'
31-
__version__ = '0.54.1'
30+
__date__ = '2025-08-12'
31+
__version__ = '0.55.0'
3232

3333
__all__ = [
3434
# Modules:

extract_msg/attachments/attachment.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import string
1414
import zipfile
1515

16-
from typing import TYPE_CHECKING
16+
from typing import Optional, TYPE_CHECKING
1717

1818
from .. import constants
1919
from .attachment_base import AttachmentBase
@@ -72,7 +72,7 @@ def getFilename(self, **kwargs) -> str:
7272

7373
return filename
7474

75-
def regenerateRandomName(self) -> str:
75+
def regenerateRandomName(self) -> None:
7676
"""
7777
Used to regenerate the random filename used if the attachment cannot
7878
find a usable filename.
@@ -166,9 +166,11 @@ def save(self, **kwargs) -> constants.SAVE_TYPE:
166166
_zip.close()
167167

168168
@property
169-
def data(self) -> bytes:
169+
def data(self) -> Optional[bytes]:
170170
"""
171171
The bytes making up the attachment data.
172+
173+
If the attachment data stream does not exist, returns None.
172174
"""
173175
return self.__data
174176

extract_msg/msg_classes/message_base.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -726,12 +726,12 @@ def save(self, **kwargs) -> constants.SAVE_TYPE:
726726
pdf = kwargs.get('pdf', False)
727727
allowFallback = kwargs.get('allowFallback', False)
728728
_zip = kwargs.get('zip')
729-
maxNameLength = kwargs.get('maxNameLength', 256)
729+
maxNameLength = kwargs.get('maxNameLength', 40)
730730

731731
# Variables involved in the save location.
732732
customFilename = kwargs.get('customFilename')
733733
useMsgFilename = kwargs.get('useMsgFilename', False)
734-
#maxPathLength = kwargs.get('maxPathLength', 255)
734+
#maxPathLength = kwargs.get('maxPathLength', 255) # TODO
735735

736736
# Track if we are only saving the attachments.
737737
attachOnly = kwargs.get('attachmentsOnly', False)
@@ -742,6 +742,8 @@ def save(self, **kwargs) -> constants.SAVE_TYPE:
742742
# raising an exception.
743743
skipBodyNotFound = kwargs.get('skipBodyNotFound', False)
744744

745+
fext = None
746+
745747
if pdf:
746748
kwargs['preparedHtml'] = True
747749

@@ -758,30 +760,22 @@ def save(self, **kwargs) -> constants.SAVE_TYPE:
758760
if self.htmlBody:
759761
useHtml = True
760762
fext = 'html'
761-
elif not allowFallback:
762-
if skipBodyNotFound:
763-
fext = None
764-
else:
765-
raise DataNotFoundError('Could not find the htmlBody.')
763+
elif not allowFallback and not skipBodyNotFound:
764+
raise DataNotFoundError('Could not find the htmlBody.')
766765

767766
if pdf:
768767
if self.htmlBody:
769768
usePdf = True
770769
fext = 'pdf'
771-
elif not allowFallback:
772-
if skipBodyNotFound:
773-
fext = None
774-
else:
775-
raise DataNotFoundError('Count not find the htmlBody to convert to pdf.')
770+
elif not allowFallback and not skipBodyNotFound:
771+
raise DataNotFoundError('Count not find the htmlBody to convert to pdf.')
776772

777773
if rtf or (html and not useHtml) or (pdf and not usePdf):
778774
if self.rtfBody:
779775
useRtf = True
780776
fext = 'rtf'
781777
elif not allowFallback:
782-
if skipBodyNotFound:
783-
fext = None
784-
else:
778+
if not skipBodyNotFound:
785779
raise DataNotFoundError('Could not find the rtfBody.')
786780
else:
787781
# This was the last resort before plain text, so fall
@@ -794,10 +788,7 @@ def save(self, **kwargs) -> constants.SAVE_TYPE:
794788
# We need to check if the plain text body was found. If it
795789
# was found but was empty that is considered valid, so we
796790
# specifically check against None.
797-
if self.body is None:
798-
if skipBodyNotFound:
799-
fext = None
800-
else:
791+
if self.body is None and not skipBodyNotFound:
801792
if allowFallback:
802793
raise DataNotFoundError('Could not find a valid body using current options.')
803794
else:
@@ -872,12 +863,12 @@ def save(self, **kwargs) -> constants.SAVE_TYPE:
872863
if not _zip:
873864
try:
874865
os.makedirs(path)
875-
except Exception:
866+
except Exception as e:
876867
newDirName = addNumToDir(path)
877868
if newDirName:
878869
path = newDirName
879870
else:
880-
raise OSError(f'Failed to create directory "{path}". Does it already exist?')
871+
raise OSError(f'Failed to create directory "{path}". Reason: {e}')
881872
else:
882873
# In my testing I ended up with multiple files in a zip at the
883874
# same location so let's try to handle that.

extract_msg/open_msg.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def openMsg(path, **kwargs) -> MSGFile:
109109
# lower function. So let's make sure we got a good return first.
110110
if not ct:
111111
if kwargs.get('strict', True):
112+
msg.close()
112113
raise InvalidFileFormatError('File was confirmed to be an olefile, but was not an MSG file.')
113114
else:
114115
# If strict mode is off, we'll just return an MSGFile anyways.

0 commit comments

Comments
 (0)