diff options
author | morpheus65535 <[email protected]> | 2024-05-11 23:22:55 -0400 |
---|---|---|
committer | morpheus65535 <[email protected]> | 2024-05-11 23:22:55 -0400 |
commit | 86d34039a35387e33663f14b30a65cc1165b4fc7 (patch) | |
tree | f705dda4032cf885d7aac8afcc5ddfa7c97ed11b /libs/apprise/config/file.py | |
parent | 006ee0f63ac39dc1e73c761a161aacfc6d62b380 (diff) | |
download | bazarr-86d34039a35387e33663f14b30a65cc1165b4fc7.tar.gz bazarr-86d34039a35387e33663f14b30a65cc1165b4fc7.zip |
Updated apprise to 1.8.0v1.4.3-beta.36
Diffstat (limited to 'libs/apprise/config/file.py')
-rw-r--r-- | libs/apprise/config/file.py | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/libs/apprise/config/file.py b/libs/apprise/config/file.py new file mode 100644 index 000000000..9f29ca20b --- /dev/null +++ b/libs/apprise/config/file.py @@ -0,0 +1,177 @@ +# -*- coding: utf-8 -*- +# BSD 2-Clause License +# +# Apprise - Push Notification Library. +# Copyright (c) 2024, Chris Caron <[email protected]> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import re +import os +from .base import ConfigBase +from ..common import ConfigFormat +from ..common import ContentIncludeMode +from ..locale import gettext_lazy as _ + + +class ConfigFile(ConfigBase): + """ + A wrapper for File based configuration sources + """ + + # The default descriptive name associated with the service + service_name = _('Local File') + + # The default protocol + protocol = 'file' + + # Configuration file inclusion can only be of the same type + allow_cross_includes = ContentIncludeMode.STRICT + + def __init__(self, path, **kwargs): + """ + Initialize File Object + + headers can be a dictionary of key/value pairs that you want to + additionally include as part of the server headers to post with + + """ + super().__init__(**kwargs) + + # Store our file path as it was set + self.path = os.path.abspath(os.path.expanduser(path)) + + # Update the config path to be relative to our file we just loaded + self.config_path = os.path.dirname(self.path) + + return + + def url(self, privacy=False, *args, **kwargs): + """ + Returns the URL built dynamically based on specified arguments. + """ + + # Prepare our cache value + if isinstance(self.cache, bool) or not self.cache: + cache = 'yes' if self.cache else 'no' + + else: + cache = int(self.cache) + + # Define any URL parameters + params = { + 'encoding': self.encoding, + 'cache': cache, + } + + if self.config_format: + # A format was enforced; make sure it's passed back with the url + params['format'] = self.config_format + + return 'file://{path}{params}'.format( + path=self.quote(self.path), + params='?{}'.format(self.urlencode(params)) if params else '', + ) + + def read(self, **kwargs): + """ + Perform retrieval of the configuration based on the specified request + """ + + response = None + + try: + if self.max_buffer_size > 0 and \ + os.path.getsize(self.path) > self.max_buffer_size: + + # Content exceeds maximum buffer size + self.logger.error( + 'File size exceeds maximum allowable buffer length' + ' ({}KB).'.format(int(self.max_buffer_size / 1024))) + return None + + except OSError: + # getsize() can throw this acception if the file is missing + # and or simply isn't accessible + self.logger.error( + 'File is not accessible: {}'.format(self.path)) + return None + + # Always call throttle before any server i/o is made + self.throttle() + + try: + with open(self.path, "rt", encoding=self.encoding) as f: + # Store our content for parsing + response = f.read() + + except (ValueError, UnicodeDecodeError): + # A result of our strict encoding check; if we receive this + # then the file we're opening is not something we can + # understand the encoding of.. + + self.logger.error( + 'File not using expected encoding ({}) : {}'.format( + self.encoding, self.path)) + return None + + except (IOError, OSError): + # IOError is present for backwards compatibility with Python + # versions older then 3.3. >= 3.3 throw OSError now. + + # Could not open and/or read the file; this is not a problem since + # we scan a lot of default paths. + self.logger.error( + 'File can not be opened for read: {}'.format(self.path)) + return None + + # Detect config format based on file extension if it isn't already + # enforced + if self.config_format is None and \ + re.match(r'^.*\.ya?ml\s*$', self.path, re.I) is not None: + + # YAML Filename Detected + self.default_config_format = ConfigFormat.YAML + + # Return our response object + return response + + @staticmethod + def parse_url(url): + """ + Parses the URL so that we can handle all different file paths + and return it as our path object + + """ + + results = ConfigBase.parse_url(url, verify_host=False) + if not results: + # We're done early; it's not a good URL + return results + + match = re.match(r'[a-z0-9]+://(?P<path>[^?]+)(\?.*)?', url, re.I) + if not match: + return None + + results['path'] = ConfigFile.unquote(match.group('path')) + return results |