diff --git a/CHANGELOG.md b/CHANGELOG.md index 05b3a513..9fba9641 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +**v0.30.4** +* [[TeamMsgExtractor #233](https://github.com/TeamMsgExtractor/msg-extractor/issues/233)] Added option to `Message.save` to only save the attachments (`attachmentsOnly`). This can also be used from the command line with the option `--attachments-only`. +* Corrected error string for incompatible options in `Message.save`. +* Fixed if blocks being nested weirdly in `Message.save` instead of being chained with `elif`. Probably an artifact left from adding support for HTML and RTF. + **v0.30.3** * [[TeamMsgExtractor #232](https://github.com/TeamMsgExtractor/msg-extractor/issues/232)] Updated list of known MSG class types so the module would correctly give `UnsupportedMSGTypeError` instead of `UnrecognizedMSGTypeError`. diff --git a/README.rst b/README.rst index 73780474..8a5395f7 100644 --- a/README.rst +++ b/README.rst @@ -206,8 +206,8 @@ And thank you to everyone who has opened an issue and helped us track down those .. |License: GPL v3| image:: https://img.shields.io/badge/License-GPLv3-blue.svg :target: LICENSE.txt -.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.30.3-blue.svg - :target: https://pypi.org/project/extract-msg/0.30.3/ +.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.30.4-blue.svg + :target: https://pypi.org/project/extract-msg/0.30.4/ .. |PyPI2| image:: https://img.shields.io/badge/python-3.6+-brightgreen.svg :target: https://www.python.org/downloads/release/python-367/ diff --git a/extract_msg/__init__.py b/extract_msg/__init__.py index 4aa04b4e..08c8b0aa 100644 --- a/extract_msg/__init__.py +++ b/extract_msg/__init__.py @@ -28,7 +28,7 @@ __author__ = 'Destiny Peterson & Matthew Walker' __date__ = '2022-01-28' -__version__ = '0.30.3' +__version__ = '0.30.4' import logging diff --git a/extract_msg/__main__.py b/extract_msg/__main__.py index 23cb93e5..76201caa 100644 --- a/extract_msg/__main__.py +++ b/extract_msg/__main__.py @@ -49,15 +49,16 @@ def main() -> None: # Quickly make a dictionary for the keyword arguments. kwargs = { - 'customPath': out, - 'customFilename': args.out_name, - 'json': args.json, - 'useMsgFilename': args.use_filename, + 'allowFallback': args.allowFallback, + 'attachmentsOnly': args.attachmentsOnly, 'contentId': args.cid, + 'customFilename': args.out_name, + 'customPath': out, 'html': args.html, - 'rtf': args.rtf, - 'allowFallback': args.allowFallback, + 'json': args.json, 'preparedHtml': args.preparedHtml, + 'rtf': args.rtf, + 'useMsgFilename': args.use_filename, 'zip': args.zip, } diff --git a/extract_msg/message.py b/extract_msg/message.py index ca2ad760..2494b867 100644 --- a/extract_msg/message.py +++ b/extract_msg/message.py @@ -114,6 +114,9 @@ def save(self, **kwargs): useMsgFilename = kwargs.get('useMsgFilename', False) #maxPathLength = kwargs.get('maxPathLength', 255) + # Track if we are only saving the attachments. + attachOnly = kwargs.get('attachmentsOnly', False) + # ZipFile handling. if _zip: # `raw` and `zip` are incompatible. @@ -142,8 +145,8 @@ def save(self, **kwargs): kwargs['customFilename'] = None # Check if incompatible options have been provided in any way. - if _json + html + rtf + raw > 1: - raise IncompatibleOptionsError('Only one of the following options may be used at a time: toJson, raw, html, rtf') + if _json + html + rtf + raw + attachOnly > 1: + raise IncompatibleOptionsError('Only one of the following options may be used at a time: json, raw, html, rtf, attachmentsOnly.') # Get the type of line endings. crlf = inputToBytes(self.crlf, 'utf-8') @@ -213,39 +216,40 @@ def save(self, **kwargs): f.write(headerText.encode('utf-8')) try: - # Check whether we should be using HTML or RTF. - fext = 'txt' - - useHtml = False - useRtf = False - if html: - if self.htmlBody: - useHtml = True - fext = 'html' - elif not allowFallback: - raise DataNotFoundError('Could not find the htmlBody') - - if rtf or (html and not useHtml): - if self.rtfBody: - useRtf = True - fext = 'rtf' - elif not allowFallback: - raise DataNotFoundError('Could not find the rtfBody') + if not attachOnly: + # Check whether we should be using HTML or RTF. + fext = 'txt' + + useHtml = False + useRtf = False + if html: + if self.htmlBody: + useHtml = True + fext = 'html' + elif not allowFallback: + raise DataNotFoundError('Could not find the htmlBody') + + if rtf or (html and not useHtml): + if self.rtfBody: + useRtf = True + fext = 'rtf' + elif not allowFallback: + raise DataNotFoundError('Could not find the rtfBody') # Save the attachments. attachmentNames = [attachment.save(**kwargs) for attachment in self.attachments] - # Determine the extension to use for the body. - fext = 'json' if _json else fext + if not attachOnly: + # Determine the extension to use for the body. + fext = 'json' if _json else fext - with _open(str(path / ('message.' + fext)), mode) as f: - if _json: - emailObj = json.loads(self.getJson()) - emailObj['attachments'] = attachmentNames + with _open(str(path / ('message.' + fext)), mode) as f: + if _json: + emailObj = json.loads(self.getJson()) + emailObj['attachments'] = attachmentNames - f.write(inputToBytes(json.dumps(emailObj), 'utf-8')) - else: - if useHtml: + f.write(inputToBytes(json.dumps(emailObj), 'utf-8')) + elif useHtml: # Inject the header into the data and then write it to # the file. data = injectHtmlHeader(self, prepared = kwargs.get('preparedHtml', False)) diff --git a/extract_msg/utils.py b/extract_msg/utils.py index 6e0ff485..cf01e8cf 100644 --- a/extract_msg/utils.py +++ b/extract_msg/utils.py @@ -197,6 +197,9 @@ def getCommandArgs(args): # --zip parser.add_argument('--zip', dest='zip', help='Path to use for saving to a zip file.') + # --attachments-only + parser.add_argument('--attachments-only', dest='attachmentsOnly', action='store_true', + help='Specify to only save attachments from an msg file.') # --out-name NAME parser.add_argument('--out-name', dest='out_name', help='Name to be used with saving the file output. Should come immediately after the file name.') @@ -207,8 +210,8 @@ def getCommandArgs(args): options = parser.parse_args(args) # Check if more than one of the following arguments has been specified - if options.html + options.rtf + options.json + options.raw > 1: - raise IncompatibleOptionsError('Only one of these options may be selected at a time: --html, --json, --raw, --rtf') + if options.html + options.rtf + options.json + options.raw + options.attachmentsOnly > 1: + raise IncompatibleOptionsError('Only one of these options may be selected at a time: --html, --json, --raw, --rtf, --attachments-only') if options.dev or options.file_logging: options.verbose = True