Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'certbot-nginx/certbot_nginx/_internal/obj.py')
-rw-r--r--certbot-nginx/certbot_nginx/_internal/obj.py263
1 files changed, 263 insertions, 0 deletions
diff --git a/certbot-nginx/certbot_nginx/_internal/obj.py b/certbot-nginx/certbot_nginx/_internal/obj.py
new file mode 100644
index 000000000..1a92c8b37
--- /dev/null
+++ b/certbot-nginx/certbot_nginx/_internal/obj.py
@@ -0,0 +1,263 @@
+"""Module contains classes used by the Nginx Configurator."""
+import re
+
+import six
+
+from certbot.plugins import common
+
+ADD_HEADER_DIRECTIVE = 'add_header'
+
+class Addr(common.Addr):
+ r"""Represents an Nginx address, i.e. what comes after the 'listen'
+ directive.
+
+ According to the `documentation`_, this may be address[:port], port,
+ or unix:path. The latter is ignored here.
+
+ The default value if no directive is specified is \*:80 (superuser)
+ or \*:8000 (otherwise). If no port is specified, the default is
+ 80. If no address is specified, listen on all addresses.
+
+ .. _documentation:
+ http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
+
+ .. todo:: Old-style nginx configs define SSL vhosts in a separate
+ block instead of using 'ssl' in the listen directive.
+
+ :param str addr: addr part of vhost address, may be hostname, IPv4, IPv6,
+ "", or "\*"
+ :param str port: port number or "\*" or ""
+ :param bool ssl: Whether the directive includes 'ssl'
+ :param bool default: Whether the directive includes 'default_server'
+ :param bool default: Whether this is an IPv6 address
+ :param bool ipv6only: Whether the directive includes 'ipv6only=on'
+
+ """
+ UNSPECIFIED_IPV4_ADDRESSES = ('', '*', '0.0.0.0')
+ CANONICAL_UNSPECIFIED_ADDRESS = UNSPECIFIED_IPV4_ADDRESSES[0]
+
+ def __init__(self, host, port, ssl, default, ipv6, ipv6only):
+ super(Addr, self).__init__((host, port))
+ self.ssl = ssl
+ self.default = default
+ self.ipv6 = ipv6
+ self.ipv6only = ipv6only
+ self.unspecified_address = host in self.UNSPECIFIED_IPV4_ADDRESSES
+
+ @classmethod
+ def fromstring(cls, str_addr):
+ """Initialize Addr from string."""
+ parts = str_addr.split(' ')
+ ssl = False
+ default = False
+ ipv6 = False
+ ipv6only = False
+ host = ''
+ port = ''
+
+ # The first part must be the address
+ addr = parts.pop(0)
+
+ # Ignore UNIX-domain sockets
+ if addr.startswith('unix:'):
+ return None
+
+ # IPv6 check
+ ipv6_match = re.match(r'\[.*\]', addr)
+ if ipv6_match:
+ ipv6 = True
+ # IPv6 handling
+ host = ipv6_match.group()
+ # The rest of the addr string will be the port, if any
+ port = addr[ipv6_match.end()+1:]
+ else:
+ # IPv4 handling
+ tup = addr.partition(':')
+ if re.match(r'^\d+$', tup[0]):
+ # This is a bare port, not a hostname. E.g. listen 80
+ host = ''
+ port = tup[0]
+ else:
+ # This is a host-port tuple. E.g. listen 127.0.0.1:*
+ host = tup[0]
+ port = tup[2]
+
+ # The rest of the parts are options; we only care about ssl and default
+ while parts:
+ nextpart = parts.pop()
+ if nextpart == 'ssl':
+ ssl = True
+ elif nextpart == 'default_server':
+ default = True
+ elif nextpart == 'default':
+ default = True
+ elif nextpart == "ipv6only=on":
+ ipv6only = True
+
+ return cls(host, port, ssl, default, ipv6, ipv6only)
+
+ def to_string(self, include_default=True):
+ """Return string representation of Addr"""
+ parts = ''
+ if self.tup[0] and self.tup[1]:
+ parts = "%s:%s" % self.tup
+ elif self.tup[0]:
+ parts = self.tup[0]
+ else:
+ parts = self.tup[1]
+
+ if self.default and include_default:
+ parts += ' default_server'
+ if self.ssl:
+ parts += ' ssl'
+
+ return parts
+
+ def __str__(self):
+ return self.to_string()
+
+ def __repr__(self):
+ return "Addr(" + self.__str__() + ")"
+
+ def __hash__(self): # pylint: disable=useless-super-delegation
+ # Python 3 requires explicit overridden for __hash__
+ # See certbot-apache/certbot_apache/_internal/obj.py for more information
+ return super(Addr, self).__hash__()
+
+ def super_eq(self, other):
+ """Check ip/port equality, with IPv6 support.
+ """
+ # If both addresses got an unspecified address, then make sure the
+ # host representation in each match when doing the comparison.
+ if self.unspecified_address and other.unspecified_address:
+ return common.Addr((self.CANONICAL_UNSPECIFIED_ADDRESS,
+ self.tup[1]), self.ipv6) == \
+ common.Addr((other.CANONICAL_UNSPECIFIED_ADDRESS,
+ other.tup[1]), other.ipv6)
+ return super(Addr, self).__eq__(other)
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return (self.super_eq(other) and
+ self.ssl == other.ssl and
+ self.default == other.default)
+ return False
+
+
+class VirtualHost(object):
+ """Represents an Nginx Virtualhost.
+
+ :ivar str filep: file path of VH
+ :ivar set addrs: Virtual Host addresses (:class:`set` of :class:`Addr`)
+ :ivar set names: Server names/aliases of vhost
+ (:class:`list` of :class:`str`)
+ :ivar list raw: The raw form of the parsed server block
+
+ :ivar bool ssl: SSLEngine on in vhost
+ :ivar bool enabled: Virtual host is enabled
+ :ivar list path: The indices into the parsed file used to access
+ the server block defining the vhost
+
+ """
+
+ def __init__(self, filep, addrs, ssl, enabled, names, raw, path):
+ """Initialize a VH."""
+ self.filep = filep
+ self.addrs = addrs
+ self.names = names
+ self.ssl = ssl
+ self.enabled = enabled
+ self.raw = raw
+ self.path = path
+
+ def __str__(self):
+ addr_str = ", ".join(str(addr) for addr in sorted(self.addrs, key=str))
+ # names might be a set, and it has different representations in Python
+ # 2 and 3. Force it to be a list here for consistent outputs
+ return ("file: %s\n"
+ "addrs: %s\n"
+ "names: %s\n"
+ "ssl: %s\n"
+ "enabled: %s" % (self.filep, addr_str,
+ list(self.names), self.ssl, self.enabled))
+
+ def __repr__(self):
+ return "VirtualHost(" + self.__str__().replace("\n", ", ") + ")\n"
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return (self.filep == other.filep and
+ sorted(self.addrs, key=str) == sorted(other.addrs, key=str) and
+ self.names == other.names and
+ self.ssl == other.ssl and
+ self.enabled == other.enabled and
+ self.path == other.path)
+
+ return False
+
+ def __hash__(self):
+ return hash((self.filep, tuple(self.path),
+ tuple(self.addrs), tuple(self.names),
+ self.ssl, self.enabled))
+
+ def has_header(self, header_name):
+ """Determine if this server block has a particular header set.
+ :param str header_name: The name of the header to check for, e.g.
+ 'Strict-Transport-Security'
+ """
+ found = _find_directive(self.raw, ADD_HEADER_DIRECTIVE, header_name)
+ return found is not None
+
+ def contains_list(self, test):
+ """Determine if raw server block contains test list at top level
+ """
+ for i in six.moves.range(0, len(self.raw) - len(test) + 1):
+ if self.raw[i:i + len(test)] == test:
+ return True
+ return False
+
+ def ipv6_enabled(self):
+ """Return true if one or more of the listen directives in vhost supports
+ IPv6"""
+ for a in self.addrs:
+ if a.ipv6:
+ return True
+ return False
+
+ def ipv4_enabled(self):
+ """Return true if one or more of the listen directives in vhost are IPv4
+ only"""
+ if not self.addrs:
+ return True
+ for a in self.addrs:
+ if not a.ipv6:
+ return True
+ return False
+
+ def display_repr(self):
+ """Return a representation of VHost to be used in dialog"""
+ return (
+ "File: {filename}\n"
+ "Addresses: {addrs}\n"
+ "Names: {names}\n"
+ "HTTPS: {https}\n".format(
+ filename=self.filep,
+ addrs=", ".join(str(addr) for addr in self.addrs),
+ names=", ".join(self.names),
+ https="Yes" if self.ssl else "No"))
+
+def _find_directive(directives, directive_name, match_content=None):
+ """Find a directive of type directive_name in directives. If match_content is given,
+ Searches for `match_content` in the directive arguments.
+ """
+ if not directives or isinstance(directives, six.string_types):
+ return None
+
+ # If match_content is None, just match on directive type. Otherwise, match on
+ # both directive type -and- the content!
+ if directives[0] == directive_name and \
+ (match_content is None or match_content in directives):
+ return directives
+
+ matches = (_find_directive(line, directive_name, match_content) for line in directives)
+ return next((m for m in matches if m is not None), None)