This commit is contained in:
MrMeeb 2023-03-01 12:00:06 +00:00
parent 5d093db4f6
commit b643f0644b
21 changed files with 133 additions and 36 deletions

View File

@ -21,3 +21,4 @@ repos:
- id: djlint-jinja - id: djlint-jinja
files: '.*\.html' files: '.*\.html'
entry: djlint --reformat entry: djlint --reformat

View File

@ -17,7 +17,7 @@ from attr import dataclass
from app import config from app import config
from app.email import headers from app.email import headers
from app.log import LOG from app.log import LOG
from app.message_utils import message_to_bytes from app.message_utils import message_to_bytes, message_format_base64_parts
@dataclass @dataclass
@ -173,8 +173,12 @@ class MailSender:
self._save_request_to_unsent_dir(send_request) self._save_request_to_unsent_dir(send_request)
return False return False
def _save_request_to_unsent_dir(self, send_request: SendRequest): def _save_request_to_unsent_dir(
file_name = f"DeliveryFail-{int(time.time())}-{uuid.uuid4()}.{SendRequest.SAVE_EXTENSION}" self, send_request: SendRequest, prefix: str = "DeliveryFail"
):
file_name = (
f"{prefix}-{int(time.time())}-{uuid.uuid4()}.{SendRequest.SAVE_EXTENSION}"
)
file_path = os.path.join(config.SAVE_UNSENT_DIR, file_name) file_path = os.path.join(config.SAVE_UNSENT_DIR, file_name)
file_contents = send_request.to_bytes() file_contents = send_request.to_bytes()
with open(file_path, "wb") as fd: with open(file_path, "wb") as fd:
@ -256,7 +260,7 @@ def sl_sendmail(
send_request = SendRequest( send_request = SendRequest(
envelope_from, envelope_from,
envelope_to, envelope_to,
msg, message_format_base64_parts(msg),
mail_options, mail_options,
rcpt_options, rcpt_options,
is_forward, is_forward,

View File

@ -1,21 +1,42 @@
import re
from email import policy from email import policy
from email.message import Message from email.message import Message
from app.email import headers
from app.log import LOG from app.log import LOG
# Spam assassin might flag as spam with a different line length
BASE64_LINELENGTH = 76
def message_to_bytes(msg: Message) -> bytes: def message_to_bytes(msg: Message) -> bytes:
"""replace Message.as_bytes() method by trying different policies""" """replace Message.as_bytes() method by trying different policies"""
for generator_policy in [None, policy.SMTP, policy.SMTPUTF8]: for generator_policy in [None, policy.SMTP, policy.SMTPUTF8]:
try: try:
return msg.as_bytes(policy=generator_policy) return msg.as_bytes(policy=generator_policy)
except: except Exception:
LOG.w("as_bytes() fails with %s policy", policy, exc_info=True) LOG.w("as_bytes() fails with %s policy", policy, exc_info=True)
msg_string = msg.as_string() msg_string = msg.as_string()
try: try:
return msg_string.encode() return msg_string.encode()
except: except Exception:
LOG.w("as_string().encode() fails", exc_info=True) LOG.w("as_string().encode() fails", exc_info=True)
return msg_string.encode(errors="replace") return msg_string.encode(errors="replace")
def message_format_base64_parts(msg: Message) -> Message:
for part in msg.walk():
if part.get(
headers.CONTENT_TRANSFER_ENCODING
) == "base64" and part.get_content_type() in ("text/plain", "text/html"):
# Remove line breaks
body = re.sub("[\r\n]", "", part.get_payload())
# Split in 80 column lines
chunks = [
body[i : i + BASE64_LINELENGTH]
for i in range(0, len(body), BASE64_LINELENGTH)
]
part.set_payload("\r\n".join(chunks))
return msg

View File

@ -50,7 +50,9 @@
</p> </p>
<p> <p>
This Youtube video can also quickly walk you through the steps: This Youtube video can also quickly walk you through the steps:
<a href="https://www.youtube.com/watch?v=VsypF-DBaow" target="_blank"> <a href="https://www.youtube.com/watch?v=VsypF-DBaow"
target="_blank"
rel="noopener noreferrer">
How to send emails from an alias <i class="fe fe-external-link"></i> How to send emails from an alias <i class="fe fe-external-link"></i>
</a> </a>
</p> </p>

View File

@ -23,7 +23,9 @@
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
This feature is only available on Premium plan. This feature is only available on Premium plan.
<a href="{{ URL }}/dashboard/pricing" target="_blank" rel="noopener"> <a href="{{ URL }}/dashboard/pricing"
target="_blank"
rel="noopener noreferrer">
Upgrade<i class="fe fe-external-link"></i> Upgrade<i class="fe fe-external-link"></i>
</a> </a>
</div> </div>

View File

@ -78,7 +78,7 @@
data-clipboard-text=".*suffix">.*suffix</em> data-clipboard-text=".*suffix">.*suffix</em>
<br /> <br />
To test out regex, we recommend using regex tester tool like To test out regex, we recommend using regex tester tool like
<a href="https://regex101.com" target="_blank">https://regex101.com↗</a> <a href="https://regex101.com" target="_blank" rel="noopener noreferrer">https://regex101.com↗</a>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">

View File

@ -158,7 +158,7 @@
SPF SPF
<a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework" <a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework"
target="_blank" target="_blank"
rel="noopener">(Wikipedia↗)</a> rel="noopener noreferrer">(Wikipedia↗)</a>
is an email is an email
authentication method authentication method
designed to detect forging sender addresses during the delivery of the email. designed to detect forging sender addresses during the delivery of the email.
@ -229,7 +229,7 @@
DKIM DKIM
<a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail" <a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail"
target="_blank" target="_blank"
rel="noopener">(Wikipedia↗)</a> rel="noopener noreferrer">(Wikipedia↗)</a>
is an is an
email email
authentication method authentication method
@ -335,7 +335,7 @@
DMARC DMARC
<a href="https://en.wikipedia.org/wiki/DMARC" <a href="https://en.wikipedia.org/wiki/DMARC"
target="_blank" target="_blank"
rel="noopener"> rel="noopener noreferrer">
(Wikipedia↗) (Wikipedia↗)
</a> </a>
is designed to protect the domain from unauthorized use, commonly known as email spoofing. is designed to protect the domain from unauthorized use, commonly known as email spoofing.

View File

@ -72,7 +72,7 @@ PGP Encryption
</ul> </ul>
<div class="small-text"> <div class="small-text">
More information on our More information on our
<a href="https://simplelogin.io/pricing" target="_blank" rel="noopener"> <a href="https://simplelogin.io/pricing" target="_blank" rel="noopener noreferrer">
Pricing Pricing
Page <i class="fe fe-external-link"></i> Page <i class="fe fe-external-link"></i>
</a> </a>
@ -120,7 +120,7 @@ Upgrade your SimpleLogin account
<div id="normal-upgrade" class="{% if proton_upgrade %} collapse{% endif %}"> <div id="normal-upgrade" class="{% if proton_upgrade %} collapse{% endif %}">
<div class="display-6 my-3"> <div class="display-6 my-3">
🔐 Secure payments by 🔐 Secure payments by
<a href="https://paddle.com" target="_blank" rel="noopener"> <a href="https://paddle.com" target="_blank" rel="noopener noreferrer">
Paddle <i class="fe fe-external-link"></i> Paddle <i class="fe fe-external-link"></i>
</a> </a>
</div> </div>
@ -164,13 +164,13 @@ $4/month
<hr /> <hr />
<i class="fa fa-bitcoin"></i> <i class="fa fa-bitcoin"></i>
Payment via Payment via
<a href="https://commerce.coinbase.com/?lang=en" target="_blank"> <a href="https://commerce.coinbase.com/?lang=en" target="_blank" rel="noopener noreferrer">
Coinbase Commerce<i class="fe fe-external-link"></i> Coinbase Commerce<i class="fe fe-external-link"></i>
</a> </a>
<br /> <br />
Currently Bitcoin, Bitcoin Cash, Dai, Ethereum, Litecoin and USD Coin are supported. Currently Bitcoin, Bitcoin Cash, Dai, Ethereum, Litecoin and USD Coin are supported.
<br /> <br />
<a class="btn btn-outline-primary" href="{{ url_for('dashboard.coinbase_checkout_route') }}" target="_blank"> <a class="btn btn-outline-primary" href="{{ url_for('dashboard.coinbase_checkout_route') }}" target="_blank" rel="noopener noreferrer">
Yearly billing - Crypto Yearly billing - Crypto
<br /> <br />
$30/year $30/year

View File

@ -17,7 +17,8 @@
<div class="alert alert-info"> <div class="alert alert-info">
This page shows all emails that are either refused by your mailbox (bounced) or detected as spams/phishing (quarantine) via our This page shows all emails that are either refused by your mailbox (bounced) or detected as spams/phishing (quarantine) via our
<a href="https://simplelogin.io/docs/getting-started/anti-phishing/" <a href="https://simplelogin.io/docs/getting-started/anti-phishing/"
target="_blank">anti-phishing program ↗</a> target="_blank"
rel="noopener noreferrer">anti-phishing program ↗</a>
<ul class="p-4 mb-0"> <ul class="p-4 mb-0">
<li> <li>
If the email is indeed spam, this means the alias is now in the hands of a spammer, If the email is indeed spam, this means the alias is now in the hands of a spammer,
@ -26,7 +27,8 @@
<li> <li>
If the email isn't spam and your mailbox refuses the email, we recommend to create a <b>filter</b> to avoid your mailbox provider from blocking legitimate emails. Please refer to If the email isn't spam and your mailbox refuses the email, we recommend to create a <b>filter</b> to avoid your mailbox provider from blocking legitimate emails. Please refer to
<a href="https://simplelogin.io/docs/getting-started/troubleshooting/#emails-end-up-in-spam" <a href="https://simplelogin.io/docs/getting-started/troubleshooting/#emails-end-up-in-spam"
target="_blank">Setting up filter for SimpleLogin emails ↗</a> target="_blank"
rel="noopener noreferrer">Setting up filter for SimpleLogin emails ↗</a>
</li> </li>
<li> <li>
If the email is flagged as spams/phishing, this means that the sender explicitly states their emails should respect If the email is flagged as spams/phishing, this means that the sender explicitly states their emails should respect

View File

@ -73,7 +73,8 @@
Yearly plan subscribed with cryptocurrency which expires on Yearly plan subscribed with cryptocurrency which expires on
{{ coinbase_sub.end_at.format("YYYY-MM-DD") }}. {{ coinbase_sub.end_at.format("YYYY-MM-DD") }}.
<a href="{{ url_for('dashboard.coinbase_checkout_route') }}" <a href="{{ url_for('dashboard.coinbase_checkout_route') }}"
target="_blank"> target="_blank"
rel="noopener noreferrer">
Extend Subscription <i class="fe fe-external-link"></i> Extend Subscription <i class="fe fe-external-link"></i>
</a> </a>
</div> </div>

View File

@ -25,7 +25,7 @@
This feature is only available on Premium plan. This feature is only available on Premium plan.
<a href="{{ url_for('dashboard.pricing') }}" <a href="{{ url_for('dashboard.pricing') }}"
target="_blank" target="_blank"
rel="noopener"> rel="noopener noreferrer">
Upgrade<i class="fe fe-external-link"></i> Upgrade<i class="fe fe-external-link"></i>
</a> </a>
</div> </div>

View File

@ -33,6 +33,7 @@
</div> </div>
<a href="https://docs.simplelogin.io" <a href="https://docs.simplelogin.io"
target="_blank" target="_blank"
rel="noopener noreferrer"
class="btn btn-block btn-secondary mt-4"> class="btn btn-block btn-secondary mt-4">
Documentation <i class="fe fe-external-link"></i> Documentation <i class="fe fe-external-link"></i>
</a> </a>

View File

@ -10,7 +10,9 @@
<h4 class="alert-heading">Well done!</h4> <h4 class="alert-heading">Well done!</h4>
<p> <p>
Please head to our Please head to our
<a href="https://docs.simplelogin.io" target="_blank" rel="noopener"> <a href="https://docs.simplelogin.io"
target="_blank"
rel="noopener noreferrer">
documentation <i class="fe fe-external-link"></i> documentation <i class="fe fe-external-link"></i>
</a> </a>
to see how to add SIWSL into your app. to see how to add SIWSL into your app.

View File

@ -49,6 +49,7 @@
<a href="{{ url_for('developer.new_client') }}" class="btn btn-primary">New website</a> <a href="{{ url_for('developer.new_client') }}" class="btn btn-primary">New website</a>
<a href="https://docs.simplelogin.io" <a href="https://docs.simplelogin.io"
target="_blank" target="_blank"
rel="noopener noreferrer"
class="ml-2 btn btn-secondary"> class="ml-2 btn btn-secondary">
Docs <i class="fe fe-external-link"></i> Docs <i class="fe fe-external-link"></i>
</a> </a>

View File

@ -13,7 +13,9 @@
<div class="col-sm-4 col-xl-2"> <div class="col-sm-4 col-xl-2">
<div class="card"> <div class="card">
<a href="{{ client.home_url }}" target="_blank" rel="noopener"> <a href="{{ client.home_url }}"
target="_blank"
rel="noopener noreferrer">
<img class="card-img-top" src="{{ client.get_icon_url() }}"> <img class="card-img-top" src="{{ client.get_icon_url() }}">
</a> </a>
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">

View File

@ -46,7 +46,7 @@ https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
<a href="{{ link }}" <a href="{{ link }}"
class="f-fallback button" class="f-fallback button"
target="_blank" target="_blank"
rel="noopener" rel="noopener noreferrer"
style="color: #FFF; style="color: #FFF;
border-color: #3869d4; border-color: #3869d4;
border-style: solid; border-style: solid;

View File

@ -145,28 +145,28 @@
<ul class="list-group list-group-transparent list-group-white list-group-flush list-group-borderless mb-0 footer-list-group"> <ul class="list-group list-group-transparent list-group-white list-group-flush list-group-borderless mb-0 footer-list-group">
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn"> href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">
Chrome Extension Chrome Extension
</a> </a>
</li> </li>
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://addons.mozilla.org/firefox/addon/simplelogin/"> href="https://addons.mozilla.org/firefox/addon/simplelogin/">
Firefox Add-on Firefox Add-on
</a> </a>
</li> </li>
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://microsoftedge.microsoft.com/addons/detail/simpleloginreceive-sen/diacfpipniklenphgljfkmhinphjlfff"> href="https://microsoftedge.microsoft.com/addons/detail/simpleloginreceive-sen/diacfpipniklenphgljfkmhinphjlfff">
Edge Add-on Edge Add-on
</a> </a>
</li> </li>
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://apps.apple.com/app/id1494051017"> href="https://apps.apple.com/app/id1494051017">
Safari Safari
Extension Extension
@ -174,7 +174,7 @@
</li> </li>
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://apps.apple.com/app/id1494359858"> href="https://apps.apple.com/app/id1494359858">
iOS iOS
(App Store) (App Store)
@ -182,14 +182,14 @@
</li> </li>
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://play.google.com/store/apps/details?id=io.simplelogin.android"> href="https://play.google.com/store/apps/details?id=io.simplelogin.android">
Android (Play Store) Android (Play Store)
</a> </a>
</li> </li>
<li> <li>
<a class="list-group-item text-white footer-item " <a class="list-group-item text-white footer-item "
rel="noopener" rel="noopener noreferrer"
href="https://f-droid.org/en/packages/io.simplelogin.android.fdroid/"> href="https://f-droid.org/en/packages/io.simplelogin.android.fdroid/">
Android (F-Droid) Android (F-Droid)
</a> </a>

View File

@ -75,14 +75,17 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
<div class="dropdown-menu dropdown-menu-left dropdown-menu-arrow"> <div class="dropdown-menu dropdown-menu-left dropdown-menu-arrow">
<div class="dropdown-item"> <div class="dropdown-item">
<a href="https://simplelogin.io/docs/" target="_blank"> <a href="https://simplelogin.io/docs/"
target="_blank"
rel="noopener noreferrer">
Docs Docs
<i class="fa fa-external-link" aria-hidden="true"></i> <i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>
</div> </div>
<div class="dropdown-item"> <div class="dropdown-item">
<a href="https://github.com/simple-login/app/discussions" <a href="https://github.com/simple-login/app/discussions"
target="_blank"> target="_blank"
rel="noopener noreferrer">
Forum Forum
<i class="fa fa-external-link" aria-hidden="true"></i> <i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>
@ -94,7 +97,9 @@
</div> </div>
{% else %} {% else %}
<div class="nav-item"> <div class="nav-item">
<a href="https://simplelogin.io/docs/" target="_blank"> <a href="https://simplelogin.io/docs/"
target="_blank"
rel="noopener noreferrer">
Docs Docs
<i class="fa fa-external-link" aria-hidden="true"></i> <i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>

View File

@ -98,14 +98,17 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
<div class="dropdown-menu dropdown-menu-left dropdown-menu-arrow"> <div class="dropdown-menu dropdown-menu-left dropdown-menu-arrow">
<div class="dropdown-item"> <div class="dropdown-item">
<a href="https://simplelogin.io/docs/" target="_blank"> <a href="https://simplelogin.io/docs/"
target="_blank"
rel="noopener noreferrer">
Docs Docs
<i class="fa fa-external-link" aria-hidden="true"></i> <i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>
</div> </div>
<div class="dropdown-item"> <div class="dropdown-item">
<a href="https://github.com/simple-login/app/discussions" <a href="https://github.com/simple-login/app/discussions"
target="_blank"> target="_blank"
rel="noopener noreferrer">
Forum Forum
<i class="fa fa-external-link" aria-hidden="true"></i> <i class="fa fa-external-link" aria-hidden="true"></i>
</a> </a>

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,8 @@ import email
from app.email_utils import ( from app.email_utils import (
copy, copy,
) )
from app.message_utils import message_to_bytes from app.message_utils import message_to_bytes, message_format_base64_parts
from tests.utils import load_eml_file
def test_copy(): def test_copy():
@ -33,3 +34,13 @@ def test_to_bytes():
msg = email.message_from_string("éèà€") msg = email.message_from_string("éèà€")
assert message_to_bytes(msg).decode() == "\néèà€" assert message_to_bytes(msg).decode() == "\néèà€"
def test_base64_line_breaks():
msg = load_eml_file("bad_base64format.eml")
msg = message_format_base64_parts(msg)
for part in msg.walk():
if part.get("content-transfer-encoding") == "base64":
body = part.get_payload()
for line in body.splitlines():
assert len(line) <= 76