1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2025-01-24 12:47:05 +00:00

Fixed UP032 (f-string): Use f-string instead of format call

This commit is contained in:
Teal Dulcet 2025-01-08 05:13:33 -08:00
parent 8a9d137dd3
commit 93099ce8d8
12 changed files with 66 additions and 66 deletions

View File

@ -712,7 +712,7 @@ def munin_cgi(filename):
query_str = request.query_string.decode("utf-8", 'ignore') query_str = request.query_string.decode("utf-8", 'ignore')
env = {'PATH_INFO': '/{}/'.format(filename), 'REQUEST_METHOD': 'GET', 'QUERY_STRING': query_str} env = {'PATH_INFO': f'/{filename}/', 'REQUEST_METHOD': 'GET', 'QUERY_STRING': query_str}
code, binout = utils.shell('check_output', code, binout = utils.shell('check_output',
COMMAND.split(" ", 5), COMMAND.split(" ", 5),
# Using a maxsplit of 5 keeps the last arguments together # Using a maxsplit of 5 keeps the last arguments together

View File

@ -252,16 +252,16 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True)
# was set. So set has_rec_base to a clone of the current set of DNS settings, and don't update # was set. So set has_rec_base to a clone of the current set of DNS settings, and don't update
# during this process. # during this process.
has_rec_base = list(records) has_rec_base = list(records)
a_expl = "Required. May have a different value. Sets the IP address that {} resolves to for web hosting and other services besides mail. The A record must be present but its value does not affect mail delivery.".format(domain) a_expl = f"Required. May have a different value. Sets the IP address that {domain} resolves to for web hosting and other services besides mail. The A record must be present but its value does not affect mail delivery."
if domain_properties[domain]["auto"]: if domain_properties[domain]["auto"]:
if domain.startswith(("ns1.", "ns2.")): a_expl = False # omit from 'External DNS' page since this only applies if box is its own DNS server if domain.startswith(("ns1.", "ns2.")): a_expl = False # omit from 'External DNS' page since this only applies if box is its own DNS server
if domain.startswith("www."): a_expl = "Optional. Sets the IP address that {} resolves to so that the box can provide a redirect to the parent domain.".format(domain) if domain.startswith("www."): a_expl = f"Optional. Sets the IP address that {domain} resolves to so that the box can provide a redirect to the parent domain."
if domain.startswith("mta-sts."): a_expl = "Optional. MTA-STS Policy Host serving /.well-known/mta-sts.txt." if domain.startswith("mta-sts."): a_expl = "Optional. MTA-STS Policy Host serving /.well-known/mta-sts.txt."
if domain.startswith("autoconfig."): a_expl = "Provides email configuration autodiscovery support for Thunderbird Autoconfig." if domain.startswith("autoconfig."): a_expl = "Provides email configuration autodiscovery support for Thunderbird Autoconfig."
if domain.startswith("autodiscover."): a_expl = "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover." if domain.startswith("autodiscover."): a_expl = "Provides email configuration autodiscovery support for Z-Push ActiveSync Autodiscover."
defaults = [ defaults = [
(None, "A", env["PUBLIC_IP"], a_expl), (None, "A", env["PUBLIC_IP"], a_expl),
(None, "AAAA", env.get('PUBLIC_IPV6'), "Optional. Sets the IPv6 address that {} resolves to, e.g. for web hosting. (It is not necessary for receiving mail on this domain.)".format(domain)), (None, "AAAA", env.get('PUBLIC_IPV6'), f"Optional. Sets the IPv6 address that {domain} resolves to, e.g. for web hosting. (It is not necessary for receiving mail on this domain.)"),
] ]
for qname, rtype, value, explanation in defaults: for qname, rtype, value, explanation in defaults:
if value is None or value.strip() == "": continue # skip IPV6 if not set if value is None or value.strip() == "": continue # skip IPV6 if not set
@ -279,13 +279,13 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True)
if domain_properties[domain]["mail"]: if domain_properties[domain]["mail"]:
# The MX record says where email for the domain should be delivered: Here! # The MX record says where email for the domain should be delivered: Here!
if not has_rec(None, "MX", prefix="10 "): if not has_rec(None, "MX", prefix="10 "):
records.append((None, "MX", "10 {}.".format(env["PRIMARY_HOSTNAME"]), "Required. Specifies the hostname (and priority) of the machine that handles @{} mail.".format(domain))) records.append((None, "MX", "10 {}.".format(env["PRIMARY_HOSTNAME"]), f"Required. Specifies the hostname (and priority) of the machine that handles @{domain} mail."))
# SPF record: Permit the box ('mx', see above) to send mail on behalf of # SPF record: Permit the box ('mx', see above) to send mail on behalf of
# the domain, and no one else. # the domain, and no one else.
# Skip if the user has set a custom SPF record. # Skip if the user has set a custom SPF record.
if not has_rec(None, "TXT", prefix="v=spf1 "): if not has_rec(None, "TXT", prefix="v=spf1 "):
records.append((None, "TXT", 'v=spf1 mx -all', "Recommended. Specifies that only the box is permitted to send @{} mail.".format(domain))) records.append((None, "TXT", 'v=spf1 mx -all', f"Recommended. Specifies that only the box is permitted to send @{domain} mail."))
# Append the DKIM TXT record to the zone as generated by OpenDKIM. # Append the DKIM TXT record to the zone as generated by OpenDKIM.
# Skip if the user has set a DKIM record already. # Skip if the user has set a DKIM record already.
@ -294,12 +294,12 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True)
m = re.match(r'(\S+)\s+IN\s+TXT\s+\( ((?:"[^"]+"\s+)+)\)', orf.read(), re.S) m = re.match(r'(\S+)\s+IN\s+TXT\s+\( ((?:"[^"]+"\s+)+)\)', orf.read(), re.S)
val = "".join(re.findall(r'"([^"]+)"', m.group(2))) val = "".join(re.findall(r'"([^"]+)"', m.group(2)))
if not has_rec(m.group(1), "TXT", prefix="v=DKIM1; "): if not has_rec(m.group(1), "TXT", prefix="v=DKIM1; "):
records.append((m.group(1), "TXT", val, "Recommended. Provides a way for recipients to verify that this machine sent @{} mail.".format(domain))) records.append((m.group(1), "TXT", val, f"Recommended. Provides a way for recipients to verify that this machine sent @{domain} mail."))
# Append a DMARC record. # Append a DMARC record.
# Skip if the user has set a DMARC record already. # Skip if the user has set a DMARC record already.
if not has_rec("_dmarc", "TXT", prefix="v=DMARC1; "): if not has_rec("_dmarc", "TXT", prefix="v=DMARC1; "):
records.append(("_dmarc", "TXT", 'v=DMARC1; p=quarantine;', "Recommended. Specifies that mail that does not originate from the box but claims to be from @{} or which does not have a valid DKIM signature is suspect and should be quarantined by the recipient's mail system.".format(domain))) records.append(("_dmarc", "TXT", 'v=DMARC1; p=quarantine;', f"Recommended. Specifies that mail that does not originate from the box but claims to be from @{domain} or which does not have a valid DKIM signature is suspect and should be quarantined by the recipient's mail system."))
if domain_properties[domain]["user"]: if domain_properties[domain]["user"]:
# 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
@ -362,9 +362,9 @@ def build_zone(domain, domain_properties, additional_records, env, is_zone=True)
# Mark this domain as not sending mail with hard-fail SPF and DMARC records. # Mark this domain as not sending mail with hard-fail SPF and DMARC records.
d = (qname+"." if qname else "") + domain d = (qname+"." if qname else "") + domain
if not has_rec(qname, "TXT", prefix="v=spf1 "): if not has_rec(qname, "TXT", prefix="v=spf1 "):
records.append((qname, "TXT", 'v=spf1 -all', "Recommended. Prevents use of this domain name for outbound mail by specifying that no servers are valid sources for mail from @{}. If you do send email from this domain name you should either override this record such that the SPF rule does allow the originating server, or, take the recommended approach and have the box handle mail for this domain (simply add any receiving alias at this domain name to make this machine treat the domain name as one of its mail domains).".format(d))) records.append((qname, "TXT", 'v=spf1 -all', f"Recommended. Prevents use of this domain name for outbound mail by specifying that no servers are valid sources for mail from @{d}. If you do send email from this domain name you should either override this record such that the SPF rule does allow the originating server, or, take the recommended approach and have the box handle mail for this domain (simply add any receiving alias at this domain name to make this machine treat the domain name as one of its mail domains)."))
if not has_rec("_dmarc" + ("."+qname if qname else ""), "TXT", prefix="v=DMARC1; "): if not has_rec("_dmarc" + ("."+qname if qname else ""), "TXT", prefix="v=DMARC1; "):
records.append(("_dmarc" + ("."+qname if qname else ""), "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 @{}.".format(d))) records.append(("_dmarc" + ("."+qname if qname else ""), "TXT", 'v=DMARC1; p=reject;', f"Recommended. Prevents use of this domain name for outbound mail by specifying that the SPF rule should be honoured for mail from @{d}."))
# And with a null MX record (https://explained-from-first-principles.com/email/#null-mx-record) # And with a null MX record (https://explained-from-first-principles.com/email/#null-mx-record)
if not has_rec(qname, "MX"): if not has_rec(qname, "MX"):
@ -590,7 +590,7 @@ def get_dns_zonefile(zone, env):
if zone == domain: if zone == domain:
break break
else: else:
raise ValueError("{} is not a domain name that corresponds to a zone.".format(zone)) raise ValueError(f"{zone} is not a domain name that corresponds to a zone.")
nsd_zonefile = "/etc/nsd/zones/" + fn nsd_zonefile = "/etc/nsd/zones/" + fn
with open(nsd_zonefile, encoding="utf-8") as f: with open(nsd_zonefile, encoding="utf-8") as f:
@ -615,8 +615,8 @@ zone:
# and, if not a subnet, notifies to them. # and, if not a subnet, notifies to them.
for ipaddr in get_secondary_dns(additional_records, mode="xfr"): for ipaddr in get_secondary_dns(additional_records, mode="xfr"):
if "/" not in ipaddr: if "/" not in ipaddr:
nsdconf += "\n\tnotify: {} NOKEY".format(ipaddr) nsdconf += f"\n\tnotify: {ipaddr} NOKEY"
nsdconf += "\n\tprovide-xfr: {} NOKEY\n".format(ipaddr) nsdconf += f"\n\tprovide-xfr: {ipaddr} NOKEY\n"
# Check if the file is changing. If it isn't changing, # Check if the file is changing. If it isn't changing,
# return False to flag that no change was made. # return False to flag that no change was made.
@ -896,7 +896,7 @@ def set_custom_dns_record(qname, rtype, value, action, env):
else: else:
# No match. # No match.
if qname != "_secondary_nameserver": if qname != "_secondary_nameserver":
raise ValueError("{} is not a domain name or a subdomain of a domain name managed by this box.".format(qname)) raise ValueError(f"{qname} is not a domain name or a subdomain of a domain name managed by this box.")
# validate rtype # validate rtype
rtype = rtype.upper() rtype = rtype.upper()
@ -926,7 +926,7 @@ def set_custom_dns_record(qname, rtype, value, action, env):
# anything goes # anything goes
pass pass
else: else:
raise ValueError("Unknown record type '{}'.".format(rtype)) raise ValueError(f"Unknown record type '{rtype}'.")
# load existing config # load existing config
config = list(get_custom_dns_config(env)) config = list(get_custom_dns_config(env))
@ -1037,7 +1037,7 @@ def set_secondary_dns(hostnames, env):
try: try:
resolver.resolve(item, "AAAA") resolver.resolve(item, "AAAA")
except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout): except (dns.resolver.NoNameservers, dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.Timeout):
raise ValueError("Could not resolve the IP address of {}.".format(item)) raise ValueError(f"Could not resolve the IP address of {item}.")
else: else:
# Validate IP address. # Validate IP address.
try: try:
@ -1046,7 +1046,7 @@ def set_secondary_dns(hostnames, env):
else: else:
ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem ipaddress.ip_address(item[4:]) # raises a ValueError if there's a problem
except ValueError: except ValueError:
raise ValueError("'{}' is not an IPv4 or IPv6 address or subnet.".format(item[4:])) raise ValueError(f"'{item[4:]}' is not an IPv4 or IPv6 address or subnet.")
# Set. # Set.
set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env) set_custom_dns_record("_secondary_nameserver", "A", " ".join(hostnames), "set", env)

View File

@ -302,7 +302,7 @@ def scan_mail_log(env):
for date, sender, message in user_data["blocked"]: for date, sender, message in user_data["blocked"]:
if len(sender) > 64: if len(sender) > 64:
sender = sender[:32] + "" + sender[-32:] sender = sender[:32] + "" + sender[-32:]
user_rejects.extend((f'{date} - {sender} ', ' {}'.format(message))) user_rejects.extend((f'{date} - {sender} ', f' {message}'))
rejects.append(user_rejects) rejects.append(user_rejects)
print_user_table( print_user_table(
@ -608,7 +608,7 @@ def valid_date(string):
try: try:
date = dateutil.parser.parse(string) date = dateutil.parser.parse(string)
except ValueError: except ValueError:
raise argparse.ArgumentTypeError("Unrecognized date and/or time '{}'".format(string)) raise argparse.ArgumentTypeError(f"Unrecognized date and/or time '{string}'")
return date return date
@ -669,7 +669,7 @@ def print_user_table(users, data=None, sub_data=None, activity=None, latest=None
col_str = f"{d[row]!s:<20}" col_str = f"{d[row]!s:<20}"
col_left[col] = True col_left[col] = True
else: else:
temp = "{{:>{}}}".format(max(5, len(l) + 1, len(str(d[row])) + 1)) temp = f"{{:>{max(5, len(l) + 1, len(str(d[row])) + 1)}}}"
col_str = temp.format(str(d[row])) col_str = temp.format(str(d[row]))
col_widths[col] = max(col_widths[col], len(col_str)) col_widths[col] = max(col_widths[col], len(col_str))
line += col_str line += col_str
@ -706,10 +706,10 @@ def print_user_table(users, data=None, sub_data=None, activity=None, latest=None
if sub_data is not None: if sub_data is not None:
for l, d in sub_data: for l, d in sub_data:
if d[row]: if d[row]:
lines.extend(('', '{}'.format(l), '├─%s' % (len(l) * ''), '')) lines.extend(('', f'{l}', '├─%s' % (len(l) * ''), ''))
max_len = 0 max_len = 0
for v in list(d[row]): for v in list(d[row]):
lines.append("{}".format(v)) lines.append(f"{v}")
max_len = max(max_len, len(v)) max_len = max(max_len, len(v))
lines.append("" + (max_len + 1) * "") lines.append("" + (max_len + 1) * "")

View File

@ -322,7 +322,7 @@ def set_mail_password(email, pw, env):
conn, c = open_database(env, with_connection=True) conn, c = open_database(env, with_connection=True)
c.execute("UPDATE users SET password=? WHERE email=?", (pw, email)) c.execute("UPDATE users SET password=? WHERE email=?", (pw, email))
if c.rowcount != 1: if c.rowcount != 1:
return ("That's not a user ({}).".format(email), 400) return (f"That's not a user ({email}).", 400)
conn.commit() conn.commit()
return "OK" return "OK"
@ -341,7 +341,7 @@ def get_mail_password(email, env):
c.execute('SELECT password FROM users WHERE email=?', (email,)) c.execute('SELECT password FROM users WHERE email=?', (email,))
rows = c.fetchall() rows = c.fetchall()
if len(rows) != 1: if len(rows) != 1:
raise ValueError("That's not a user ({}).".format(email)) raise ValueError(f"That's not a user ({email}).")
return rows[0][0] return rows[0][0]
def remove_mail_user(email, env): def remove_mail_user(email, env):
@ -349,7 +349,7 @@ def remove_mail_user(email, env):
conn, c = open_database(env, with_connection=True) conn, c = open_database(env, with_connection=True)
c.execute("DELETE FROM users WHERE email=?", (email,)) c.execute("DELETE FROM users WHERE email=?", (email,))
if c.rowcount != 1: if c.rowcount != 1:
return ("That's not a user ({}).".format(email), 400) return (f"That's not a user ({email}).", 400)
conn.commit() conn.commit()
# Update things in case any domains are removed. # Update things in case any domains are removed.
@ -365,12 +365,12 @@ def get_mail_user_privileges(email, env, empty_on_error=False):
rows = c.fetchall() rows = c.fetchall()
if len(rows) != 1: if len(rows) != 1:
if empty_on_error: return [] if empty_on_error: return []
return ("That's not a user ({}).".format(email), 400) return (f"That's not a user ({email}).", 400)
return parse_privs(rows[0][0]) return parse_privs(rows[0][0])
def validate_privilege(priv): def validate_privilege(priv):
if "\n" in priv or priv.strip() == "": if "\n" in priv or priv.strip() == "":
return ("That's not a valid privilege ({}).".format(priv), 400) return (f"That's not a valid privilege ({priv}).", 400)
return None return None
def add_remove_mail_user_privilege(email, priv, action, env): def add_remove_mail_user_privilege(email, priv, action, env):
@ -413,7 +413,7 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist
if address == "": if address == "":
return ("No email address provided.", 400) return ("No email address provided.", 400)
if not validate_email(address, mode='alias'): if not validate_email(address, mode='alias'):
return ("Invalid email address ({}).".format(address), 400) return (f"Invalid email address ({address}).", 400)
# validate forwards_to # validate forwards_to
validated_forwards_to = [] validated_forwards_to = []
@ -442,7 +442,7 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist
# Strip any +tag from email alias and check privileges # Strip any +tag from email alias and check privileges
privileged_email = re.sub(r"(?=\+)[^@]*(?=@)",'',email) privileged_email = re.sub(r"(?=\+)[^@]*(?=@)",'',email)
if not validate_email(email): if not validate_email(email):
return ("Invalid receiver email address ({}).".format(email), 400) return (f"Invalid receiver email address ({email}).", 400)
if is_dcv_source and not is_dcv_address(email) and "admin" not in get_mail_user_privileges(privileged_email, env, empty_on_error=True): if is_dcv_source and not is_dcv_address(email) and "admin" not in get_mail_user_privileges(privileged_email, env, empty_on_error=True):
# Make domain control validation hijacking a little harder to mess up by # Make domain control validation hijacking a little harder to mess up by
# requiring aliases for email addresses typically used in DCV to forward # requiring aliases for email addresses typically used in DCV to forward
@ -462,7 +462,7 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist
login = login.strip() login = login.strip()
if login == "": continue if login == "": continue
if login not in valid_logins: if login not in valid_logins:
return ("Invalid permitted sender: {} is not a user on this system.".format(login), 400) return (f"Invalid permitted sender: {login} is not a user on this system.", 400)
validated_permitted_senders.append(login) validated_permitted_senders.append(login)
# Make sure the alias has either a forwards_to or a permitted_sender. # Make sure the alias has either a forwards_to or a permitted_sender.
@ -481,7 +481,7 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist
return_status = "alias added" return_status = "alias added"
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
if not update_if_exists: if not update_if_exists:
return ("Alias already exists ({}).".format(address), 400) return (f"Alias already exists ({address}).", 400)
c.execute("UPDATE aliases SET destination = ?, permitted_senders = ? WHERE source = ?", (forwards_to, permitted_senders, address)) c.execute("UPDATE aliases SET destination = ?, permitted_senders = ? WHERE source = ?", (forwards_to, permitted_senders, address))
return_status = "alias updated" return_status = "alias updated"
@ -500,7 +500,7 @@ def remove_mail_alias(address, env, do_kick=True):
conn, c = open_database(env, with_connection=True) conn, c = open_database(env, with_connection=True)
c.execute("DELETE FROM aliases WHERE source=?", (address,)) c.execute("DELETE FROM aliases WHERE source=?", (address,))
if c.rowcount != 1: if c.rowcount != 1:
return ("That's not an alias ({}).".format(address), 400) return (f"That's not an alias ({address}).", 400)
conn.commit() conn.commit()
if do_kick: if do_kick:

View File

@ -515,7 +515,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
cert = load_pem(ssl_cert_chain[0]) cert = load_pem(ssl_cert_chain[0])
if not isinstance(cert, Certificate): raise ValueError("This is not a certificate file.") if not isinstance(cert, Certificate): raise ValueError("This is not a certificate file.")
except ValueError as e: except ValueError as e:
return ("There is a problem with the certificate file: {}".format(str(e)), None) return (f"There is a problem with the certificate file: {str(e)}", None)
# First check that the domain name is one of the names allowed by # First check that the domain name is one of the names allowed by
# the certificate. # the certificate.
@ -540,10 +540,10 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
if (not isinstance(priv_key, rsa.RSAPrivateKey) if (not isinstance(priv_key, rsa.RSAPrivateKey)
and not isinstance(priv_key, dsa.DSAPrivateKey) and not isinstance(priv_key, dsa.DSAPrivateKey)
and not isinstance(priv_key, ec.EllipticCurvePrivateKey)): and not isinstance(priv_key, ec.EllipticCurvePrivateKey)):
return ("The private key file {} is not a private key file.".format(ssl_private_key), None) return (f"The private key file {ssl_private_key} is not a private key file.", None)
if priv_key.public_key().public_numbers() != cert.public_key().public_numbers(): if priv_key.public_key().public_numbers() != cert.public_key().public_numbers():
return ("The certificate does not correspond to the private key at {}.".format(ssl_private_key), None) return (f"The certificate does not correspond to the private key at {ssl_private_key}.", None)
# We could also use the openssl command line tool to get the modulus # We could also use the openssl command line tool to get the modulus
# listed in each file. The output of each command below looks like "Modulus=XXXXX". # listed in each file. The output of each command below looks like "Modulus=XXXXX".
@ -589,7 +589,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
if retcode != 0: if retcode != 0:
if "unable to get local issuer certificate" in verifyoutput: if "unable to get local issuer certificate" in verifyoutput:
return ("The certificate is missing an intermediate chain or the intermediate chain is incorrect or incomplete. ({})".format(verifyoutput), None) return (f"The certificate is missing an intermediate chain or the intermediate chain is incorrect or incomplete. ({verifyoutput})", None)
# There is some unknown problem. Return the `openssl verify` raw output. # There is some unknown problem. Return the `openssl verify` raw output.
return ("There is a problem with the certificate.", verifyoutput.strip()) return ("There is a problem with the certificate.", verifyoutput.strip())
@ -605,7 +605,7 @@ def check_certificate(domain, ssl_certificate, ssl_private_key, warn_if_expiring
expiry_info = "The certificate expires in %d days on %s." % (ndays, cert_expiration_date.date().isoformat()) expiry_info = "The certificate expires in %d days on %s." % (ndays, cert_expiration_date.date().isoformat())
else: else:
# We'll renew it with Lets Encrypt. # We'll renew it with Lets Encrypt.
expiry_info = "The certificate expires on {}.".format(cert_expiration_date.date().isoformat()) expiry_info = f"The certificate expires on {cert_expiration_date.date().isoformat()}."
if warn_if_expiring_soon and ndays <= warn_if_expiring_soon: if warn_if_expiring_soon and ndays <= warn_if_expiring_soon:
# Warn on day 10 to give 4 days for us to automatically renew the # Warn on day 10 to give 4 days for us to automatically renew the

View File

@ -248,7 +248,7 @@ def check_free_disk_space(rounded_values, env, output):
def check_free_memory(rounded_values, env, output): def check_free_memory(rounded_values, env, output):
# Check free memory. # Check free memory.
percent_free = 100 - psutil.virtual_memory().percent percent_free = 100 - psutil.virtual_memory().percent
memory_msg = "System memory is {}% free.".format(str(round(percent_free))) memory_msg = f"System memory is {str(round(percent_free))}% free."
if percent_free >= 20: if percent_free >= 20:
if rounded_values: memory_msg = "System free memory is at least 20%." if rounded_values: memory_msg = "System free memory is at least 20%."
output.print_ok(memory_msg) output.print_ok(memory_msg)
@ -478,7 +478,7 @@ def check_primary_hostname_dns(domain, env, output, dns_domains, dns_zonefiles):
tlsa25 = query_dns(tlsa_qname, "TLSA", nxdomain=None) tlsa25 = query_dns(tlsa_qname, "TLSA", nxdomain=None)
tlsa25_expected = build_tlsa_record(env) tlsa25_expected = build_tlsa_record(env)
if tlsa25 == tlsa25_expected: if tlsa25 == tlsa25_expected:
output.print_ok("""The DANE TLSA record for incoming mail is correct ({}).""".format(tlsa_qname),) output.print_ok(f"""The DANE TLSA record for incoming mail is correct ({tlsa_qname}).""",)
elif tlsa25 is None: elif tlsa25 is None:
if has_dnssec: if has_dnssec:
# Omit a warning about it not being set if DNSSEC isn't enabled, # Omit a warning about it not being set if DNSSEC isn't enabled,
@ -497,9 +497,9 @@ def check_alias_exists(alias_name, alias, env, output):
if mail_aliases[alias]: if mail_aliases[alias]:
output.print_ok(f"{alias_name} exists as a mail alias. [{alias}{mail_aliases[alias]}]") output.print_ok(f"{alias_name} exists as a mail alias. [{alias}{mail_aliases[alias]}]")
else: else:
output.print_error("""You must set the destination of the mail alias for {} to direct email to you or another administrator.""".format(alias)) output.print_error(f"""You must set the destination of the mail alias for {alias} to direct email to you or another administrator.""")
else: else:
output.print_error("""You must add a mail alias for {} which directs email to you or another administrator.""".format(alias)) output.print_error(f"""You must add a mail alias for {alias} which directs email to you or another administrator.""")
def check_dns_zone(domain, env, output, dns_zonefiles): def check_dns_zone(domain, env, output, dns_zonefiles):
# If a DS record is set at the registrar, check DNSSEC first because it will affect the NS query. # If a DS record is set at the registrar, check DNSSEC first because it will affect the NS query.
@ -527,7 +527,7 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
probably_external_dns = False probably_external_dns = False
if existing_ns.lower() == correct_ns.lower(): if existing_ns.lower() == correct_ns.lower():
output.print_ok("Nameservers are set correctly at registrar. [{}]".format(correct_ns)) output.print_ok(f"Nameservers are set correctly at registrar. [{correct_ns}]")
elif ip == correct_ip: elif ip == correct_ip:
# The domain resolves correctly, so maybe the user is using External DNS. # The domain resolves correctly, so maybe the user is using External DNS.
output.print_warning(f"""The nameservers set on this domain at your domain name registrar should be {correct_ns}. They are currently {existing_ns}. output.print_warning(f"""The nameservers set on this domain at your domain name registrar should be {correct_ns}. They are currently {existing_ns}.
@ -546,7 +546,7 @@ def check_dns_zone(domain, env, output, dns_zonefiles):
# We must first resolve the nameserver to an IP address so we can query it. # We must first resolve the nameserver to an IP address so we can query it.
ns_ips = query_dns(ns, "A") ns_ips = query_dns(ns, "A")
if not ns_ips or ns_ips in {'[Not Set]', '[timeout]'}: if not ns_ips or ns_ips in {'[Not Set]', '[timeout]'}:
output.print_error("Secondary nameserver {} is not valid (it doesn't resolve to an IP address).".format(ns)) output.print_error(f"Secondary nameserver {ns} is not valid (it doesn't resolve to an IP address).")
continue continue
# Choose the first IP if nameserver returns multiple # Choose the first IP if nameserver returns multiple
ns_ip = ns_ips.split('; ')[0] ns_ip = ns_ips.split('; ')[0]
@ -587,7 +587,7 @@ def check_dns_zone_suggestions(domain, env, output, dns_zonefiles, domains_with_
if domain in domains_with_a_records: if domain in domains_with_a_records:
output.print_warning("""Web has been disabled for this domain because you have set a custom DNS record.""") output.print_warning("""Web has been disabled for this domain because you have set a custom DNS record.""")
if "www." + domain in domains_with_a_records: if "www." + domain in domains_with_a_records:
output.print_warning("""A redirect from 'www.{}' has been disabled for this domain because you have set a custom DNS record on the www subdomain.""".format(domain)) output.print_warning(f"""A redirect from 'www.{domain}' has been disabled for this domain because you have set a custom DNS record on the www subdomain.""")
# Since DNSSEC is optional, if a DS record is NOT set at the registrar suggest it. # Since DNSSEC is optional, if a DS record is NOT set at the registrar suggest it.
# (If it was set, we did the check earlier.) # (If it was set, we did the check earlier.)
@ -616,7 +616,7 @@ def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary=False):
# Some registrars may want the public key so they can compute the digest. The DS # Some registrars may want the public key so they can compute the digest. The DS
# record that we suggest using is for the KSK (and that's how the DS records were generated). # record that we suggest using is for the KSK (and that's how the DS records were generated).
# We'll also give the nice name for the key algorithm. # We'll also give the nice name for the key algorithm.
dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/{}.conf'.format(alg_name_map[ds_alg]))) dnssec_keys = load_env_vars_from_file(os.path.join(env['STORAGE_ROOT'], f'dns/dnssec/{alg_name_map[ds_alg]}.conf'))
with open(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/' + dnssec_keys['KSK'] + '.key'), encoding="utf-8") as f: with open(os.path.join(env['STORAGE_ROOT'], 'dns/dnssec/' + dnssec_keys['KSK'] + '.key'), encoding="utf-8") as f:
dnsssec_pubkey = f.read().split("\t")[3].split(" ")[3] dnsssec_pubkey = f.read().split("\t")[3].split(" ")[3]
@ -662,7 +662,7 @@ def check_dnssec(domain, env, output, dns_zonefiles, is_checking_primary=False):
for this domain is valid.""") for this domain is valid.""")
else: else:
if is_checking_primary: if is_checking_primary:
output.print_error("""The DNSSEC 'DS' record for {} is incorrect. See further details below.""".format(domain)) output.print_error(f"""The DNSSEC 'DS' record for {domain} is incorrect. See further details below.""")
return return
output.print_error("""This domain's DNSSEC DS record is incorrect. The chain of trust is broken between the public DNS system output.print_error("""This domain's DNSSEC DS record is incorrect. The chain of trust is broken between the public DNS system
and this machine's DNS server. It may take several hours for public DNS to update after a change. If you did not recently and this machine's DNS server. It may take several hours for public DNS to update after a change. If you did not recently
@ -776,9 +776,9 @@ def check_mail_domain(domain, env, output):
elif dbl == "127.255.255.252": elif dbl == "127.255.255.252":
output.print_warning("Incorrect spamhaus query: {}. Could not determine whether the domain {} is blacklisted.".format(domain+'.dbl.spamhaus.org', domain)) output.print_warning("Incorrect spamhaus query: {}. Could not determine whether the domain {} is blacklisted.".format(domain+'.dbl.spamhaus.org', domain))
elif dbl == "127.255.255.254": elif dbl == "127.255.255.254":
output.print_warning("Mail-in-a-Box is configured to use a public DNS server. This is not supported by spamhaus. Could not determine whether the domain {} is blacklisted.".format(domain)) output.print_warning(f"Mail-in-a-Box is configured to use a public DNS server. This is not supported by spamhaus. Could not determine whether the domain {domain} is blacklisted.")
elif dbl == "127.255.255.255": elif dbl == "127.255.255.255":
output.print_warning("Too many queries have been performed on the spamhaus server. Could not determine whether the domain {} is blacklisted.".format(domain)) output.print_warning(f"Too many queries have been performed on the spamhaus server. Could not determine whether the domain {domain} is blacklisted.")
else: else:
output.print_error(f"""This domain is listed in the Spamhaus Domain Block List (code {dbl}), output.print_error(f"""This domain is listed in the Spamhaus Domain Block List (code {dbl}),
which may prevent recipients from receiving your mail. which may prevent recipients from receiving your mail.
@ -960,14 +960,14 @@ def check_miab_version(env, output):
this_ver = "Unknown" this_ver = "Unknown"
if config.get("privacy", True): if config.get("privacy", True):
output.print_warning("You are running version Mail-in-a-Box {}. Mail-in-a-Box version check disabled by privacy setting.".format(this_ver)) output.print_warning(f"You are running version Mail-in-a-Box {this_ver}. Mail-in-a-Box version check disabled by privacy setting.")
else: else:
latest_ver = get_latest_miab_version() latest_ver = get_latest_miab_version()
if this_ver == latest_ver: if this_ver == latest_ver:
output.print_ok("Mail-in-a-Box is up to date. You are running version {}.".format(this_ver)) output.print_ok(f"Mail-in-a-Box is up to date. You are running version {this_ver}.")
elif latest_ver is None: elif latest_ver is None:
output.print_error("Latest Mail-in-a-Box version could not be determined. You are running version {}.".format(this_ver)) output.print_error(f"Latest Mail-in-a-Box version could not be determined. You are running version {this_ver}.")
else: else:
output.print_error(f"A new version of Mail-in-a-Box is available. You are running version {this_ver}. The latest version is {latest_ver}. For upgrade instructions, see https://mailinabox.email. ") output.print_error(f"A new version of Mail-in-a-Box is available. You are running version {this_ver}. The latest version is {latest_ver}. For upgrade instructions, see https://mailinabox.email. ")

View File

@ -180,8 +180,8 @@ def make_domain_config(domain, templates, ssl_certificates, env):
web_sockets = True web_sockets = True
url = re.sub(r"#(.*)$", "", url) url = re.sub(r"#(.*)$", "", url)
nginx_conf_extra += "\tlocation {} {{".format(path) nginx_conf_extra += f"\tlocation {path} {{"
nginx_conf_extra += "\n\t\tproxy_pass {};".format(url) nginx_conf_extra += f"\n\t\tproxy_pass {url};"
if proxy_redirect_off: if proxy_redirect_off:
nginx_conf_extra += "\n\t\tproxy_redirect off;" nginx_conf_extra += "\n\t\tproxy_redirect off;"
if pass_http_host_header: if pass_http_host_header:
@ -198,8 +198,8 @@ def make_domain_config(domain, templates, ssl_certificates, env):
nginx_conf_extra += "\n\t\tproxy_set_header X-Real-IP $remote_addr;" 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 {} {{".format(path) nginx_conf_extra += f"\tlocation {path} {{"
nginx_conf_extra += "\n\t\talias {};".format(alias) nginx_conf_extra += f"\n\t\talias {alias};"
nginx_conf_extra += "\n\t}\n" nginx_conf_extra += "\n\t}\n"
for path, url in yaml.get("redirects", {}).items(): for path, url in yaml.get("redirects", {}).items():
nginx_conf_extra += f"\trewrite {path} {url} permanent;\n" nginx_conf_extra += f"\trewrite {path} {url} permanent;\n"
@ -216,7 +216,7 @@ def make_domain_config(domain, templates, ssl_certificates, env):
# Add in any user customizations in the includes/ folder. # Add in any user customizations in the includes/ folder.
nginx_conf_custom_include = os.path.join(env["STORAGE_ROOT"], "www", safe_domain_name(domain) + ".conf") nginx_conf_custom_include = os.path.join(env["STORAGE_ROOT"], "www", safe_domain_name(domain) + ".conf")
if os.path.exists(nginx_conf_custom_include): if os.path.exists(nginx_conf_custom_include):
nginx_conf_extra += "\tinclude {};\n".format(nginx_conf_custom_include) nginx_conf_extra += f"\tinclude {nginx_conf_custom_include};\n"
# PUT IT ALL TOGETHER # PUT IT ALL TOGETHER
# Combine the pieces. Iteratively place each template into the "# ADDITIONAL DIRECTIVES HERE" placeholder # Combine the pieces. Iteratively place each template into the "# ADDITIONAL DIRECTIVES HERE" placeholder

View File

@ -164,7 +164,7 @@ def migration_12(env):
try: try:
table = table[0] table = table[0]
c = conn.cursor() c = conn.cursor()
dropcmd = "DROP TABLE {}".format(table) dropcmd = f"DROP TABLE {table}"
c.execute(dropcmd) c.execute(dropcmd)
except: except:
print("Failed to drop table", table) print("Failed to drop table", table)

View File

@ -142,7 +142,7 @@ def http_test(url, expected_status, postdata=None, qsargs=None, auth=None):
# return response status code # return response status code
if r.status_code != expected_status: if r.status_code != expected_status:
r.raise_for_status() # anything but 200 r.raise_for_status() # anything but 200
raise OSError("Got unexpected status code {}.".format(r.status_code)) raise OSError(f"Got unexpected status code {r.status_code}.")
# define how to run a test # define how to run a test

View File

@ -51,7 +51,7 @@ def test2(tests, server, description):
response = dns.resolver.resolve(qname, rtype) response = dns.resolver.resolve(qname, rtype)
except dns.resolver.NoNameservers: except dns.resolver.NoNameservers:
# host did not have an answer for this query # host did not have an answer for this query
print("Could not connect to {} for DNS query.".format(server)) print(f"Could not connect to {server} for DNS query.")
sys.exit(1) sys.exit(1)
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
# host did not have an answer for this query; not sure what the # host did not have an answer for this query; not sure what the
@ -79,7 +79,7 @@ def test2(tests, server, description):
# Test the response from the machine itself. # Test the response from the machine itself.
if not test(ipaddr, "Mail-in-a-Box"): if not test(ipaddr, "Mail-in-a-Box"):
print () print ()
print ("Please run the Mail-in-a-Box setup script on {} again.".format(hostname)) print (f"Please run the Mail-in-a-Box setup script on {hostname} again.")
sys.exit(1) sys.exit(1)
else: else:
print ("The Mail-in-a-Box provided correct DNS answers.") print ("The Mail-in-a-Box provided correct DNS answers.")
@ -89,7 +89,7 @@ else:
# to see if the machine is hooked up to recursive DNS properly. # to see if the machine is hooked up to recursive DNS properly.
if not test("8.8.8.8", "Google Public DNS"): if not test("8.8.8.8", "Google Public DNS"):
print () print ()
print ("Check that the nameserver settings for {} are correct at your domain registrar. It may take a few hours for Google Public DNS to update after changes on your Mail-in-a-Box.".format(hostname)) print (f"Check that the nameserver settings for {hostname} are correct at your domain registrar. It may take a few hours for Google Public DNS to update after changes on your Mail-in-a-Box.")
sys.exit(1) sys.exit(1)
else: else:
print ("Your domain registrar or DNS host appears to be configured correctly as well. Public DNS provides the same answers.") print ("Your domain registrar or DNS host appears to be configured correctly as well. Public DNS provides the same answers.")

View File

@ -46,7 +46,7 @@ reverse_ip = dns.reversename.from_address(ipaddr) # e.g. "1.0.0.127.in-addr.arpa
try: try:
reverse_dns = dns.resolver.resolve(reverse_ip, 'PTR')[0].target.to_text(omit_final_dot=True) # => hostname reverse_dns = dns.resolver.resolve(reverse_ip, 'PTR')[0].target.to_text(omit_final_dot=True) # => hostname
except dns.resolver.NXDOMAIN: except dns.resolver.NXDOMAIN:
print("Reverse DNS lookup failed for {}. SMTP EHLO name check skipped.".format(ipaddr)) print(f"Reverse DNS lookup failed for {ipaddr}. SMTP EHLO name check skipped.")
reverse_dns = None reverse_dns = None
if reverse_dns is not None: if reverse_dns is not None:
server.ehlo_or_helo_if_needed() # must send EHLO before getting the server's EHLO name server.ehlo_or_helo_if_needed() # must send EHLO before getting the server's EHLO name
@ -54,7 +54,7 @@ if reverse_dns is not None:
if helo_name != reverse_dns: if helo_name != reverse_dns:
print("The server's EHLO name does not match its reverse hostname. Check DNS settings.") print("The server's EHLO name does not match its reverse hostname. Check DNS settings.")
else: else:
print("SMTP EHLO name ({}) is OK.".format(helo_name)) print(f"SMTP EHLO name ({helo_name}) is OK.")
# Login and send a test email. # Login and send a test email.
server.login(emailaddress, pw) server.login(emailaddress, pw)

View File

@ -229,7 +229,7 @@ class EditConf(Grammar):
for opt in re.split("\s+", self[4].string): for opt in re.split("\s+", self[4].string):
k, v = opt.split("=", 1) k, v = opt.split("=", 1)
v = re.sub(r"\n+", "", fixup_tokens(v)) # not sure why newlines are getting doubled v = re.sub(r"\n+", "", fixup_tokens(v)) # not sure why newlines are getting doubled
options.append("{}{}{}".format(k, eq, v)) options.append(f"{k}{eq}{v}")
return "<div class='write-to'><div class='filename'>" + self[1].string + " <span>(change settings)</span></div><pre>" + "\n".join(cgi.escape(s) for s in options) + "</pre></div>\n" return "<div class='write-to'><div class='filename'>" + self[1].string + " <span>(change settings)</span></div><pre>" + "\n".join(cgi.escape(s) for s in options) + "</pre></div>\n"
class CaptureOutput(Grammar): class CaptureOutput(Grammar):
@ -247,7 +247,7 @@ class SedReplace(Grammar):
class EchoPipe(Grammar): class EchoPipe(Grammar):
grammar = OPTIONAL(SPACE), L("echo "), REST_OF_LINE, L(' | '), REST_OF_LINE, EOL grammar = OPTIONAL(SPACE), L("echo "), REST_OF_LINE, L(' | '), REST_OF_LINE, EOL
def value(self): def value(self):
text = " ".join("\"{}\"".format(s) for s in self[2].string.split(" ")) text = " ".join(f"\"{s}\"" for s in self[2].string.split(" "))
return "<pre class='shell'><div>echo " + recode_bash(text) + " \<br> | " + recode_bash(self[4].string) + "</div></pre>\n" return "<pre class='shell'><div>echo " + recode_bash(text) + " \<br> | " + recode_bash(self[4].string) + "</div></pre>\n"
def shell_line(bash): def shell_line(bash):
@ -427,7 +427,7 @@ class BashScript(Grammar):
mode = 0 mode = 0
clz = "contd" clz = "contd"
if mode == 0: if mode == 0:
v += "<div class='row {}'>\n".format(clz) v += f"<div class='row {clz}'>\n"
v += "<div class='col-md-6 prose'>\n" v += "<div class='col-md-6 prose'>\n"
v += item v += item
mode = 1 mode = 1