mirror of
				https://github.com/mail-in-a-box/mailinabox.git
				synced 2025-11-03 19:30:54 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/mail-in-a-box/mailinabox
# Conflicts: # README.md # management/mailconfig.py # management/web_update.py
This commit is contained in:
		
						commit
						27c1b93bcf
					
				
							
								
								
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@ -9,6 +9,17 @@ Mail:
 | 
				
			|||||||
* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed.
 | 
					* An MTA-STS policy for incoming mail is now published (in DNS and over HTTPS) when the primary hostname and email address domain both have a signed TLS certificate installed.
 | 
				
			||||||
* MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname.
 | 
					* MTA-STS reporting is enabled with reports sent to administrator@ the primary hostname.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DNS:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* autoconfig and autodiscover subdomains and CalDAV/CardDAV SRV records are no longer generated for domains that don't have user accounts since they are unnecessary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					v0.46 (June 11, 2020)
 | 
				
			||||||
 | 
					---------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Security fixes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Roundcube is updated to version 1.4.6 (https://roundcube.net/news/2020/06/02/security-updates-1.4.5-and-1.3.12).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
v0.45 (May 16, 2020)
 | 
					v0.45 (May 16, 2020)
 | 
				
			||||||
--------------------
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -30,7 +30,6 @@ On MiaB-LDAP, a one-time change must be applied manually to allow the remote Nex
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Under-the-Hood Details
 | 
					## Under-the-Hood Details
 | 
				
			||||||
-------
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Additional directory in user-data**
 | 
					**Additional directory in user-data**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,10 +16,10 @@ if [ `date "+%u"` -eq 1 ]; then
 | 
				
			|||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Take a backup.
 | 
					# Take a backup.
 | 
				
			||||||
management/backup.py | management/email_administrator.py "Backup Status"
 | 
					management/backup.py 2>&1 | management/email_administrator.py "Backup Status"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Provision any new certificates for new domains or domains with expiring certificates.
 | 
					# Provision any new certificates for new domains or domains with expiring certificates.
 | 
				
			||||||
management/ssl_certificates.py -q | management/email_administrator.py "TLS Certificate Provisioning Result"
 | 
					management/ssl_certificates.py -q  2>&1 | management/email_administrator.py "TLS Certificate Provisioning Result"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Run status checks and email the administrator if anything changed.
 | 
					# Run status checks and email the administrator if anything changed.
 | 
				
			||||||
management/status_checks.py --show-changes | management/email_administrator.py "Status Checks Change Notice"
 | 
					management/status_checks.py --show-changes  2>&1 | management/email_administrator.py "Status Checks Change Notice"
 | 
				
			||||||
 | 
				
			|||||||
@ -282,28 +282,30 @@ def build_zone(domain, all_domains, additional_records, www_redirect_domains, en
 | 
				
			|||||||
		if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "):
 | 
							if not has_rec(dmarc_qname, "TXT", prefix="v=DMARC1; "):
 | 
				
			||||||
			records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain)))
 | 
								records.append((dmarc_qname, "TXT", 'v=DMARC1; p=reject', "Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @%s." % (qname + "." + domain)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname.
 | 
						# Add CardDAV/CalDAV SRV records on the non-primary hostname that points to the primary hostname
 | 
				
			||||||
 | 
						# for autoconfiguration of mail clients (so only domains hosting user accounts need it).
 | 
				
			||||||
	# The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot).
 | 
						# The SRV record format is priority (0, whatever), weight (0, whatever), port, service provider hostname (w/ trailing dot).
 | 
				
			||||||
	if domain != env["PRIMARY_HOSTNAME"]:
 | 
						if domain != env["PRIMARY_HOSTNAME"] and domain in get_mail_domains(env, users_only=True):
 | 
				
			||||||
		for dav in ("card", "cal"):
 | 
							for dav in ("card", "cal"):
 | 
				
			||||||
			qname = "_" + dav + "davs._tcp"
 | 
								qname = "_" + dav + "davs._tcp"
 | 
				
			||||||
			if not has_rec(qname, "SRV"):
 | 
								if not has_rec(qname, "SRV"):
 | 
				
			||||||
				records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain."))
 | 
									records.append((qname, "SRV", "0 0 443 " + env["PRIMARY_HOSTNAME"] + ".", "Recommended. Specifies the hostname of the server that handles CardDAV/CalDAV services for email addresses on this domain."))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# Adds autoconfiguration A records for all domains.
 | 
						# Adds autoconfiguration A records for all domains that there are user accounts at.
 | 
				
			||||||
	# This allows the following clients to automatically configure email addresses in the respective applications.
 | 
						# This allows the following clients to automatically configure email addresses in the respective applications.
 | 
				
			||||||
	# autodiscover.* - Z-Push ActiveSync Autodiscover
 | 
						# autodiscover.* - Z-Push ActiveSync Autodiscover
 | 
				
			||||||
	# autoconfig.* - Thunderbird Autoconfig
 | 
						# autoconfig.* - Thunderbird Autoconfig
 | 
				
			||||||
	autodiscover_records = [
 | 
						if domain in get_mail_domains(env, users_only=True):
 | 
				
			||||||
		("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."),
 | 
							autodiscover_records = [
 | 
				
			||||||
		("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."),
 | 
								("autodiscover", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."),
 | 
				
			||||||
		("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig."),
 | 
								("autodiscover", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."),
 | 
				
			||||||
		("autoconfig", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.")
 | 
								("autoconfig", "A", env["PUBLIC_IP"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig."),
 | 
				
			||||||
	]
 | 
								("autoconfig", "AAAA", env["PUBLIC_IPV6"], "Provides email configuration autodiscovery support for Thunderbird Autoconfig.")
 | 
				
			||||||
	for qname, rtype, value, explanation in autodiscover_records:
 | 
							]
 | 
				
			||||||
		if value is None or value.strip() == "": continue # skip IPV6 if not set
 | 
							for qname, rtype, value, explanation in autodiscover_records:
 | 
				
			||||||
		if not has_rec(qname, rtype):
 | 
								if value is None or value.strip() == "": continue # skip IPV6 if not set
 | 
				
			||||||
			records.append((qname, rtype, value, explanation))
 | 
								if not has_rec(qname, rtype):
 | 
				
			||||||
 | 
									records.append((qname, rtype, value, explanation))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# If this is a domain name that there are email addresses configured for, i.e. "something@"
 | 
						# If this is a domain name that there are email addresses configured for, i.e. "something@"
 | 
				
			||||||
	# this domain name, then the domain name is a MTA-STS (https://tools.ietf.org/html/rfc8461)
 | 
						# this domain name, then the domain name is a MTA-STS (https://tools.ietf.org/html/rfc8461)
 | 
				
			||||||
 | 
				
			|||||||
@ -432,7 +432,7 @@ def get_domain(emailaddr, as_unicode=True):
 | 
				
			|||||||
			pass
 | 
								pass
 | 
				
			||||||
	return ret
 | 
						return ret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_mail_domains(env, as_map=False, filter_aliases=None, category=None):
 | 
					def get_mail_domains(env, as_map=False, filter_aliases=None, category=None, users_only=False):
 | 
				
			||||||
	# Retrieves all domains, IDNA-encoded, we accept mail for.
 | 
						# Retrieves all domains, IDNA-encoded, we accept mail for.
 | 
				
			||||||
	#
 | 
						#
 | 
				
			||||||
	# If as_map is False, the function returns the lowercase domain
 | 
						# If as_map is False, the function returns the lowercase domain
 | 
				
			||||||
@ -453,7 +453,12 @@ def get_mail_domains(env, as_map=False, filter_aliases=None, category=None):
 | 
				
			|||||||
	# category is another type of filter. Set to a string value to
 | 
						# category is another type of filter. Set to a string value to
 | 
				
			||||||
	# return only those domains of that category. ie. the
 | 
						# return only those domains of that category. ie. the
 | 
				
			||||||
	# "businessCategory" attribute of the domain must include this
 | 
						# "businessCategory" attribute of the domain must include this
 | 
				
			||||||
	# category.
 | 
						# category. [TODO: this doesn't really belong there, it is here to
 | 
				
			||||||
 | 
						# make it easy for dns_update to get ssl domains]
 | 
				
			||||||
 | 
						#
 | 
				
			||||||
 | 
						# If users_only is True, only return domains with email addresses
 | 
				
			||||||
 | 
						# that correspond to user accounts. [TODO: This currently has no
 | 
				
			||||||
 | 
						# effect - this function only returns user mail domains]
 | 
				
			||||||
	#
 | 
						#
 | 
				
			||||||
	conn = open_database(env)
 | 
						conn = open_database(env)
 | 
				
			||||||
	filter = "(&(objectClass=domain)(businessCategory=mail))"
 | 
						filter = "(&(objectClass=domain)(businessCategory=mail))"
 | 
				
			||||||
 | 
				
			|||||||
@ -26,15 +26,14 @@ def get_web_domains(env, include_www_redirects=True, exclude_dns_elsewhere=True,
 | 
				
			|||||||
		# the topmost of each domain we serve.
 | 
							# the topmost of each domain we serve.
 | 
				
			||||||
		domains |= set('www.' + zone for zone, zonefile in get_dns_zones(env))
 | 
							domains |= set('www.' + zone for zone, zonefile in get_dns_zones(env))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# Add Autoconfiguration domains, allowing us to serve correct SSL certs.
 | 
						# Add Autoconfiguration domains for domains that there are user accounts at:
 | 
				
			||||||
	# 'autoconfig.' for Mozilla Thunderbird auto setup.
 | 
						# 'autoconfig.' for Mozilla Thunderbird auto setup.
 | 
				
			||||||
	# 'autodiscover.' for Activesync autodiscovery.
 | 
						# 'autodiscover.' for Activesync autodiscovery.
 | 
				
			||||||
	if 'mail' in categories:
 | 
						if 'mail' in categories:
 | 
				
			||||||
		domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env, category='mail'))
 | 
							domains |= set('autoconfig.' + maildomain for maildomain in get_mail_domains(env, users_only=True))
 | 
				
			||||||
		domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env, category='mail'))
 | 
							domains |= set('autodiscover.' + maildomain for maildomain in get_mail_domains(env, users_only=True))
 | 
				
			||||||
 | 
							# 'mta-sts.' for MTA-STS support for all domains that have email addresses.
 | 
				
			||||||
	# 'mta-sts.' for MTA-STS support.
 | 
							domains |= set('mta-sts.' + maildomain for maildomain in get_mail_domains(env))
 | 
				
			||||||
	domains |= set('mta-sts.' + maildomain for maildomain in get_mail_domains(env))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if exclude_dns_elsewhere:
 | 
						if exclude_dns_elsewhere:
 | 
				
			||||||
		# ...Unless the domain has an A/AAAA record that maps it to a different
 | 
							# ...Unless the domain has an A/AAAA record that maps it to a different
 | 
				
			||||||
@ -161,9 +160,23 @@ def make_domain_config(domain, templates, ssl_certificates, env):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			# any proxy or redirect here?
 | 
								# any proxy or redirect here?
 | 
				
			||||||
			for path, url in yaml.get("proxies", {}).items():
 | 
								for path, url in yaml.get("proxies", {}).items():
 | 
				
			||||||
 | 
									# Parse some flags in the fragment of the URL.
 | 
				
			||||||
 | 
									pass_http_host_header = False
 | 
				
			||||||
 | 
									m = re.search("#(.*)$", url)
 | 
				
			||||||
 | 
									if m:
 | 
				
			||||||
 | 
										for flag in m.group(1).split(","):
 | 
				
			||||||
 | 
											if flag == "pass-http-host":
 | 
				
			||||||
 | 
												pass_http_host_header = True
 | 
				
			||||||
 | 
										url = re.sub("#(.*)$", "", url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				nginx_conf_extra += "\tlocation %s {" % path
 | 
									nginx_conf_extra += "\tlocation %s {" % path
 | 
				
			||||||
				nginx_conf_extra += "\n\t\tproxy_pass %s;" % url
 | 
									nginx_conf_extra += "\n\t\tproxy_pass %s;" % url
 | 
				
			||||||
 | 
									if pass_http_host_header:
 | 
				
			||||||
 | 
										nginx_conf_extra += "\n\t\tproxy_set_header Host $http_host;"
 | 
				
			||||||
				nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"
 | 
									nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;"
 | 
				
			||||||
 | 
									nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Host $http_host;"
 | 
				
			||||||
 | 
									nginx_conf_extra += "\n\t\tproxy_set_header X-Forwarded-Proto $scheme;"
 | 
				
			||||||
 | 
									nginx_conf_extra += "\n\t\tproxy_set_header X-Real-IP $remote_addr;"
 | 
				
			||||||
				nginx_conf_extra += "\n\t}\n"
 | 
									nginx_conf_extra += "\n\t}\n"
 | 
				
			||||||
			for path, alias in yaml.get("aliases", {}).items():
 | 
								for path, alias in yaml.get("aliases", {}).items():
 | 
				
			||||||
				nginx_conf_extra += "\tlocation %s {" % path
 | 
									nginx_conf_extra += "\tlocation %s {" % path
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ if [ -z "$TAG" ]; then
 | 
				
			|||||||
	# want to display in status checks.
 | 
						# want to display in status checks.
 | 
				
			||||||
	if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then
 | 
						if [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/18\.04\.[0-9]/18.04/' `" == "Ubuntu 18.04 LTS" ]; then
 | 
				
			||||||
		# This machine is running Ubuntu 18.04.
 | 
							# This machine is running Ubuntu 18.04.
 | 
				
			||||||
		TAG=v0.45
 | 
							TAG=v0.46
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then
 | 
						elif [ "`lsb_release -d | sed 's/.*:\s*//' | sed 's/14\.04\.[0-9]/14.04/' `" == "Ubuntu 14.04 LTS" ]; then
 | 
				
			||||||
		# This machine is running Ubuntu 14.04.
 | 
							# This machine is running Ubuntu 14.04.
 | 
				
			||||||
 | 
				
			|||||||
@ -137,7 +137,14 @@ function get_default_privateip {
 | 
				
			|||||||
function ufw_allow {
 | 
					function ufw_allow {
 | 
				
			||||||
	if [ -z "${DISABLE_FIREWALL:-}" ]; then
 | 
						if [ -z "${DISABLE_FIREWALL:-}" ]; then
 | 
				
			||||||
		# ufw has completely unhelpful output
 | 
							# ufw has completely unhelpful output
 | 
				
			||||||
		ufw allow $1 > /dev/null;
 | 
							ufw allow "$1" > /dev/null;
 | 
				
			||||||
 | 
						fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function ufw_limit {
 | 
				
			||||||
 | 
						if [ -z "${DISABLE_FIREWALL:-}" ]; then
 | 
				
			||||||
 | 
							# ufw has completely unhelpful output
 | 
				
			||||||
 | 
							ufw limit "$1" > /dev/null;
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -256,7 +256,7 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then
 | 
				
			|||||||
	apt_install ufw
 | 
						apt_install ufw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# Allow incoming connections to SSH.
 | 
						# Allow incoming connections to SSH.
 | 
				
			||||||
	ufw_allow ssh;
 | 
						ufw_limit ssh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC
 | 
						# ssh might be running on an alternate port. Use sshd -T to dump sshd's #NODOC
 | 
				
			||||||
	# settings, find the port it is supposedly running on, and open that port #NODOC
 | 
						# settings, find the port it is supposedly running on, and open that port #NODOC
 | 
				
			||||||
@ -266,7 +266,7 @@ if [ -z "${DISABLE_FIREWALL:-}" ]; then
 | 
				
			|||||||
	if [ "$SSH_PORT" != "22" ]; then
 | 
						if [ "$SSH_PORT" != "22" ]; then
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	echo Opening alternate SSH port $SSH_PORT. #NODOC
 | 
						echo Opening alternate SSH port $SSH_PORT. #NODOC
 | 
				
			||||||
	ufw_allow $SSH_PORT #NODOC
 | 
						ufw_limit $SSH_PORT #NODOC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
	fi
 | 
						fi
 | 
				
			||||||
 | 
				
			|||||||
@ -29,8 +29,8 @@ apt_install \
 | 
				
			|||||||
# Install Roundcube from source if it is not already present or if it is out of date.
 | 
					# Install Roundcube from source if it is not already present or if it is out of date.
 | 
				
			||||||
# Combine the Roundcube version number with the commit hash of plugins to track
 | 
					# Combine the Roundcube version number with the commit hash of plugins to track
 | 
				
			||||||
# whether we have the latest version of everything.
 | 
					# whether we have the latest version of everything.
 | 
				
			||||||
VERSION=1.4.4
 | 
					VERSION=1.4.6
 | 
				
			||||||
HASH=4e425263f5bec27d39c07bde524f421bda205c07
 | 
					HASH=44961ef62bb9c9875141ca34704bbc7d6f36373d
 | 
				
			||||||
PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435
 | 
					PERSISTENT_LOGIN_VERSION=6b3fc450cae23ccb2f393d0ef67aa319e877e435
 | 
				
			||||||
HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5
 | 
					HTML5_NOTIFIER_VERSION=4b370e3cd60dabd2f428a26f45b677ad1b7118d5
 | 
				
			||||||
CARDDAV_VERSION=3.0.3
 | 
					CARDDAV_VERSION=3.0.3
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,7 @@ def generate_documentation():
 | 
				
			|||||||
	    	}
 | 
						    	}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    	.prose {
 | 
						    	.prose {
 | 
				
			||||||
	    		padding-top: 1em;    	
 | 
						    		padding-top: 1em;
 | 
				
			||||||
	    		padding-bottom: 1em;
 | 
						    		padding-bottom: 1em;
 | 
				
			||||||
	    	}
 | 
						    	}
 | 
				
			||||||
	    	.terminal {
 | 
						    	.terminal {
 | 
				
			||||||
@ -261,6 +261,10 @@ class UfwAllow(Grammar):
 | 
				
			|||||||
	grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL)
 | 
						grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL)
 | 
				
			||||||
	def value(self):
 | 
						def value(self):
 | 
				
			||||||
		return shell_line("ufw allow " + self[2].string)
 | 
							return shell_line("ufw allow " + self[2].string)
 | 
				
			||||||
 | 
					class UfwLimit(Grammar):
 | 
				
			||||||
 | 
						grammar = (ZERO_OR_MORE(SPACE), L("ufw_limit "), REST_OF_LINE, EOL)
 | 
				
			||||||
 | 
						def value(self):
 | 
				
			||||||
 | 
							return shell_line("ufw limit " + self[2].string)
 | 
				
			||||||
class RestartService(Grammar):
 | 
					class RestartService(Grammar):
 | 
				
			||||||
	grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL)
 | 
						grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL)
 | 
				
			||||||
	def value(self):
 | 
						def value(self):
 | 
				
			||||||
@ -275,7 +279,7 @@ class OtherLine(Grammar):
 | 
				
			|||||||
		return "<pre class='shell'><div>" + recode_bash(self.string.strip()) + "</div></pre>\n"
 | 
							return "<pre class='shell'><div>" + recode_bash(self.string.strip()) + "</div></pre>\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BashElement(Grammar):
 | 
					class BashElement(Grammar):
 | 
				
			||||||
	grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | RestartService | OtherLine
 | 
						grammar = Comment | CatEOF | EchoPipe | EchoLine | HideOutput | EditConf | SedReplace | AptGet | UfwAllow | UfwLimit | RestartService | OtherLine
 | 
				
			||||||
	def value(self):
 | 
						def value(self):
 | 
				
			||||||
		return self[0].value()
 | 
							return self[0].value()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user