From 0a655ecd76841f20271c714d6c747175551814b5 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Tue, 30 Aug 2022 22:53:39 -0700 Subject: [PATCH 1/9] Update funding links --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3f902216..dca3202b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ['https://www.buymeacoffee.com/DestructionE', 'https://www.patreon.com/DestructionE'] +custom: ['https://www.buymeacoffee.com/DestructionE', 'https://www.patreon.com/DestructionE', 'https://ko-fi.com/settings?tab=profile'] From e7af11b3333d14a46d9afc60bb9c4c2a5fa60b80 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Mon, 12 Sep 2022 06:48:29 -0700 Subject: [PATCH 2/9] Fix funding link --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index dca3202b..fea47b13 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: ['https://www.buymeacoffee.com/DestructionE', 'https://www.patreon.com/DestructionE', 'https://ko-fi.com/settings?tab=profile'] +custom: ['https://www.buymeacoffee.com/DestructionE', 'https://www.patreon.com/DestructionE', 'https://ko-fi.com/destructione'] From a751aa4de28482c8cf852b4a36cd230af92fade2 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Tue, 13 Sep 2022 13:01:33 -0700 Subject: [PATCH 3/9] Fix funding using available services --- .github/FUNDING.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index fea47b13..9143decb 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,3 @@ -custom: ['https://www.buymeacoffee.com/DestructionE', 'https://www.patreon.com/DestructionE', 'https://ko-fi.com/destructione'] +ko_fi: DestructionE +patreon: DestructionE +custom: ['https://www.buymeacoffee.com/DestructionE'] From 4f94b042140e1c25f04183232fbcc71dee78a1d4 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Tue, 13 Sep 2022 13:02:13 -0700 Subject: [PATCH 4/9] Fix second error for TeamMsgExtractor#291 --- CHANGELOG.md | 4 ++++ README.rst | 11 ++++++++--- extract_msg/__init__.py | 4 ++-- extract_msg/msg.py | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35cbdb04..b06696c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +**v0.36.4** +* [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)] Fixed typo in `MSGFile.saveRaw` that may have existed for a significant amount of time. It was using the wrong function (same name, but with different capitalization) but was hidden until `MSGFile` stopped being derived from `OleFileIO`. +* Updating funding information on GitHub and the `README` with more ways to support the module's development. + **v0.36.3** * Added an option to skip the body if it could not be found, rather than throwing an error. This will cause no file to be made for it in the event no valid body exists. For the save functions, this option is `skipBodyNotFound` and from the command line the option is `--skip-body-not-found`. * Fixed a bug that caused contacts to save the business phone with two colons instead of 1. diff --git a/README.rst b/README.rst index 10efd19d..6d42dca9 100644 --- a/README.rst +++ b/README.rst @@ -147,7 +147,10 @@ Supporting The Module --------------------- If you'd like to donate to help support the development of the module, you can -donate to Destiny at her Buy Me a Coffee page here: `Buy Me a Coffee`_ +donate to Destiny using one of the following services: +* `Buy Me a Coffee`_ +* `Ko-fi`_ +* `Patreon`_ Installation ------------ @@ -227,8 +230,8 @@ your access to the newest major version of extract-msg. .. |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.36.3-blue.svg - :target: https://pypi.org/project/extract-msg/0.36.3/ +.. |PyPI3| image:: https://img.shields.io/badge/pypi-0.36.4-blue.svg + :target: https://pypi.org/project/extract-msg/0.36.5/ .. |PyPI2| image:: https://img.shields.io/badge/python-3.6+-brightgreen.svg :target: https://www.python.org/downloads/release/python-367/ @@ -242,4 +245,6 @@ your access to the newest major version of extract-msg. .. _Seamus Tuohy: https://github.com/seamustuohy .. _Discord: https://discord.com/invite/B77McRmzdc .. _Buy Me a Coffee: https://www.buymeacoffee.com/DestructionE +.. _Ko-fi: https://ko-fi.com/destructione +.. _Patreon: https://www.patreon.com/DestructionE .. _msg-explorer: https://pypi.org/project/msg-explorer/ diff --git a/extract_msg/__init__.py b/extract_msg/__init__.py index 27bd84f3..c855c043 100644 --- a/extract_msg/__init__.py +++ b/extract_msg/__init__.py @@ -27,8 +27,8 @@ # along with this program. If not, see . __author__ = 'Destiny Peterson & Matthew Walker' -__date__ = '2022-08-29' -__version__ = '0.36.3' +__date__ = '2022-09-13' +__version__ = '0.36.4' import logging diff --git a/extract_msg/msg.py b/extract_msg/msg.py index 8bd39366..863aa133 100644 --- a/extract_msg/msg.py +++ b/extract_msg/msg.py @@ -523,7 +523,7 @@ def saveRaw(self, path): raise FileExistsError(f'File "{path}" already exists.') with zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED) as zfile: # Loop through all the directories - for dir_ in self.listdir(): + for dir_ in self.listDir(): sysdir = '/'.join(dir_) code = dir_[-1][-8:] if constants.PROPERTIES.get(code): From 5ca0e0a951fb7f44b7e23d94a6bf6e32486ce64b Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Tue, 13 Sep 2022 13:22:19 -0700 Subject: [PATCH 5/9] Fixed issues and added more logging to `MessageBase.getSavePdfBody` --- CHANGELOG.md | 2 ++ extract_msg/message_base.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b06696c2..ff4e3dc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ **v0.36.4** * [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)] Fixed typo in `MSGFile.saveRaw` that may have existed for a significant amount of time. It was using the wrong function (same name, but with different capitalization) but was hidden until `MSGFile` stopped being derived from `OleFileIO`. +* Added logging code to `MessageBase.getSavePdfBody` to log the list that is going to be used to run `wkhtmltopdf`. This is mainly for debugging purposes, to allow users to potentially see why their arguments may be failing. * Updating funding information on GitHub and the `README` with more ways to support the module's development. +* Fixed one of the exceptions in `MessageBase.getSavePdfBody` not using an fstring which caused it to omit information. **v0.36.3** * Added an option to skip the body if it could not be found, rather than throwing an error. This will cause no file to be made for it in the event no valid body exists. For the save functions, this option is `skipBodyNotFound` and from the command line the option is `--skip-body-not-found`. diff --git a/extract_msg/message_base.py b/extract_msg/message_base.py index 3e963fd0..d4a0da6c 100644 --- a/extract_msg/message_base.py +++ b/extract_msg/message_base.py @@ -381,7 +381,7 @@ def getSavePdfBody(self, **kwargs) -> bytes: # fail if it is not possible. parsedWkOptions = [*wkOptions] except TypeError: - raise TypeError(':param wkOptions: must be an iterable, not {type(wkOptions)}.') + raise TypeError(f':param wkOptions: must be an iterable, not {type(wkOptions)}.') else: parsedWkOptions = [] @@ -394,7 +394,9 @@ def getSavePdfBody(self, **kwargs) -> bytes: # the data will go in and come out through stdin and stdout, # respectively. This way we don't have to write temporary # files to the disk. We also ask that it be quiet about it. - process = subprocess.Popen([wkPath, *parsedWkOptions, '-', '-'], shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) + processArgs = [wkPath, *parsedWkOptions, '-', '-'] + logger.info(f'Converting to PDF with the following arguments: {processArgs}') + process = subprocess.Popen(processArgs, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) # Give the program the data and wait for the program to # finish. output = process.communicate(self.getSaveHtmlBody(**kwargs)) From 85fe8daf1409f69d9b6aad00c345aee85d922cc2 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Fri, 16 Sep 2022 15:21:23 -0700 Subject: [PATCH 6/9] Partially adjusted the code to help with TeamMsgExtractor#291 --- CHANGELOG.md | 1 + extract_msg/__init__.py | 2 +- extract_msg/message_base.py | 37 +++++++++++++++++++++++-------------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4e3dc4..433d1210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ **v0.36.4** * [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)] Fixed typo in `MSGFile.saveRaw` that may have existed for a significant amount of time. It was using the wrong function (same name, but with different capitalization) but was hidden until `MSGFile` stopped being derived from `OleFileIO`. +* [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)] Adjusted subprocess call to `wkhtmltopdf` to help user having issues with `subprocess.Popen` using a list for the arguments. `wkhtmltopdf` acted like it was given no arguments at all. * Added logging code to `MessageBase.getSavePdfBody` to log the list that is going to be used to run `wkhtmltopdf`. This is mainly for debugging purposes, to allow users to potentially see why their arguments may be failing. * Updating funding information on GitHub and the `README` with more ways to support the module's development. * Fixed one of the exceptions in `MessageBase.getSavePdfBody` not using an fstring which caused it to omit information. diff --git a/extract_msg/__init__.py b/extract_msg/__init__.py index c855c043..1740b28b 100644 --- a/extract_msg/__init__.py +++ b/extract_msg/__init__.py @@ -27,7 +27,7 @@ # along with this program. If not, see . __author__ = 'Destiny Peterson & Matthew Walker' -__date__ = '2022-09-13' +__date__ = '2022-09-16' __version__ = '0.36.4' import logging diff --git a/extract_msg/message_base.py b/extract_msg/message_base.py index d4a0da6c..46168a00 100644 --- a/extract_msg/message_base.py +++ b/extract_msg/message_base.py @@ -372,34 +372,43 @@ def getSavePdfBody(self, **kwargs) -> bytes: # Immediately try to find the executable. wkPath = findWk(kwargs.get('wkPath')) - # First thing is first, we need to parse our wkOptions if - # they exist. + # First thing is first, we need to parse our wkOptions if they exist. wkOptions = kwargs.get('wkOptions') if wkOptions: try: - # Try to convert to a list, whatever it is, and - # fail if it is not possible. + # Try to convert to a list, whatever it is, and fail if it is + # not possible. parsedWkOptions = [*wkOptions] except TypeError: raise TypeError(f':param wkOptions: must be an iterable, not {type(wkOptions)}.') else: parsedWkOptions = [] - # Confirm that all of our options we now have are either - # strings or bytes. + # Confirm that all of our options we now have are either strings or + # bytes. if not all(isinstance(option, (str, bytes)) for option in parsedWkOptions): raise TypeError(':param wkOptions: must be an iterable of strings and bytes.') - # We call the program to convert the html, but give tell it - # the data will go in and come out through stdin and stdout, - # respectively. This way we don't have to write temporary - # files to the disk. We also ask that it be quiet about it. - processArgs = [wkPath, *parsedWkOptions, '-', '-'] + # TeamMsgExtractor#291 showed a user who had issues with the list of + # args to Popen. I couldn't replicate it, but for some reason making it + # a string fixed the issue. + processArgs = ''.join(f'"{x}"' if ' ' in x and x[0] != '"' + for x in (wkPath, *parsedWkOptions, '-', '-')) + # Log the arguments. logger.info(f'Converting to PDF with the following arguments: {processArgs}') + + # Get the html body *before* calling Popen. + htmlBody = self.getSaveHtmlBody(**kwargs) + + # We call the program to convert the html, but give tell it the data + # will go in and come out through stdin and stdout, respectively. This + # way we don't have to write temporary files to the disk. We also ask + # that it be quiet about it. process = subprocess.Popen(processArgs, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) - # Give the program the data and wait for the program to - # finish. - output = process.communicate(self.getSaveHtmlBody(**kwargs)) + # Give the program the data and wait for the program to finish. + output = process.communicate(htmlBody) + + # If it errored, throw it as an exception. if process.returncode != 0: raise WKError(output[1].decode('utf-8')) From a4e8ea1b21512b3938fe634d720582c9bab2688e Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Fri, 16 Sep 2022 16:01:37 -0700 Subject: [PATCH 7/9] Setup test for new subprocess code (list to string disabled) --- extract_msg/message_base.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/extract_msg/message_base.py b/extract_msg/message_base.py index 46168a00..167fb113 100644 --- a/extract_msg/message_base.py +++ b/extract_msg/message_base.py @@ -392,11 +392,13 @@ def getSavePdfBody(self, **kwargs) -> bytes: # TeamMsgExtractor#291 showed a user who had issues with the list of # args to Popen. I couldn't replicate it, but for some reason making it # a string fixed the issue. - processArgs = ''.join(f'"{x}"' if ' ' in x and x[0] != '"' - for x in (wkPath, *parsedWkOptions, '-', '-')) + #processArgs = ' '.join(f'"{x}"' if ' ' in x and x[0] != '"' else x + # for x in (wkPath, *parsedWkOptions, '-', '-')) + processArgs = [wkPath, *parsedWkOptions, '-', '-'] # Log the arguments. logger.info(f'Converting to PDF with the following arguments: {processArgs}') + # Get the html body *before* calling Popen. htmlBody = self.getSaveHtmlBody(**kwargs) @@ -404,15 +406,15 @@ def getSavePdfBody(self, **kwargs) -> bytes: # will go in and come out through stdin and stdout, respectively. This # way we don't have to write temporary files to the disk. We also ask # that it be quiet about it. - process = subprocess.Popen(processArgs, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) + process = subprocess.run(processArgs, input = htmlBody, stdout = subprocess.PIPE, stderr = subprocess.PIPE) # Give the program the data and wait for the program to finish. - output = process.communicate(htmlBody) + #output = process.communicate(htmlBody) # If it errored, throw it as an exception. if process.returncode != 0: - raise WKError(output[1].decode('utf-8')) + raise WKError(process.stderr.decode('utf-8')) - return output[0] + return process.stdout def getSaveRtfBody(self, **kwargs) -> bytes: """ From bdd24fd8793f45fc5ceedab63780eddb8effd921 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Fri, 16 Sep 2022 16:12:19 -0700 Subject: [PATCH 8/9] Prepare to switch back to string for subprocess (finished encoding) --- extract_msg/message_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extract_msg/message_base.py b/extract_msg/message_base.py index 167fb113..c6fa5e8c 100644 --- a/extract_msg/message_base.py +++ b/extract_msg/message_base.py @@ -393,7 +393,7 @@ def getSavePdfBody(self, **kwargs) -> bytes: # args to Popen. I couldn't replicate it, but for some reason making it # a string fixed the issue. #processArgs = ' '.join(f'"{x}"' if ' ' in x and x[0] != '"' else x - # for x in (wkPath, *parsedWkOptions, '-', '-')) + # for x in map(os.fsdecode, (wkPath, *parsedWkOptions, '-', '-'))) processArgs = [wkPath, *parsedWkOptions, '-', '-'] # Log the arguments. logger.info(f'Converting to PDF with the following arguments: {processArgs}') From 357b5a4577ea6f28f5853fb1f120ff79084a9f18 Mon Sep 17 00:00:00 2001 From: TheElementalOfDestruction Date: Fri, 30 Sep 2022 20:10:31 -0700 Subject: [PATCH 9/9] v0.36.4 --- CHANGELOG.md | 2 +- README.rst | 2 +- extract_msg/__init__.py | 2 +- extract_msg/message_base.py | 5 ----- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433d1210..f8b23020 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ **v0.36.4** * [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)] Fixed typo in `MSGFile.saveRaw` that may have existed for a significant amount of time. It was using the wrong function (same name, but with different capitalization) but was hidden until `MSGFile` stopped being derived from `OleFileIO`. -* [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)] Adjusted subprocess call to `wkhtmltopdf` to help user having issues with `subprocess.Popen` using a list for the arguments. `wkhtmltopdf` acted like it was given no arguments at all. * Added logging code to `MessageBase.getSavePdfBody` to log the list that is going to be used to run `wkhtmltopdf`. This is mainly for debugging purposes, to allow users to potentially see why their arguments may be failing. * Updating funding information on GitHub and the `README` with more ways to support the module's development. * Fixed one of the exceptions in `MessageBase.getSavePdfBody` not using an fstring which caused it to omit information. +* Changed the way `wkhtmltopdf` is called to patch a possible security vulnerability. This also seems to have fixed [[TeamMsgExtractor #291](https://github.com/TeamMsgExtractor/msg-extractor/issues/291)]. **v0.36.3** * Added an option to skip the body if it could not be found, rather than throwing an error. This will cause no file to be made for it in the event no valid body exists. For the save functions, this option is `skipBodyNotFound` and from the command line the option is `--skip-body-not-found`. diff --git a/README.rst b/README.rst index 6d42dca9..a7597151 100644 --- a/README.rst +++ b/README.rst @@ -231,7 +231,7 @@ your access to the newest major version of extract-msg. :target: LICENSE.txt .. |PyPI3| image:: https://img.shields.io/badge/pypi-0.36.4-blue.svg - :target: https://pypi.org/project/extract-msg/0.36.5/ + :target: https://pypi.org/project/extract-msg/0.36.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 1740b28b..6c970f5c 100644 --- a/extract_msg/__init__.py +++ b/extract_msg/__init__.py @@ -27,7 +27,7 @@ # along with this program. If not, see . __author__ = 'Destiny Peterson & Matthew Walker' -__date__ = '2022-09-16' +__date__ = '2022-09-29' __version__ = '0.36.4' import logging diff --git a/extract_msg/message_base.py b/extract_msg/message_base.py index c6fa5e8c..4491d67e 100644 --- a/extract_msg/message_base.py +++ b/extract_msg/message_base.py @@ -389,11 +389,6 @@ def getSavePdfBody(self, **kwargs) -> bytes: if not all(isinstance(option, (str, bytes)) for option in parsedWkOptions): raise TypeError(':param wkOptions: must be an iterable of strings and bytes.') - # TeamMsgExtractor#291 showed a user who had issues with the list of - # args to Popen. I couldn't replicate it, but for some reason making it - # a string fixed the issue. - #processArgs = ' '.join(f'"{x}"' if ' ' in x and x[0] != '"' else x - # for x in map(os.fsdecode, (wkPath, *parsedWkOptions, '-', '-'))) processArgs = [wkPath, *parsedWkOptions, '-', '-'] # Log the arguments. logger.info(f'Converting to PDF with the following arguments: {processArgs}')