def test_send_mail(loop, fake_account, msg_test, settings): loop.run_until_complete(storage.start()) from_addr = mailutils.parse_email("*****@*****.**") to_addrs = [mailutils.parse_email("*****@*****.**")] # First with bad recipient result = loop.run_until_complete( smtp.send_mail(fake_account, msg_test, from_addr, to_addrs)) print(result) assert result["delivery_status"] == { "*****@*****.**": { "reason": "SMTP_ERROR", "smtp_info": "(554, b'5.5.1 Error: no inbox for this email')", "status": "ERROR", } } # Then good recipient to_addrs = [mailutils.parse_email("*****@*****.**")] result = loop.run_until_complete( smtp.send_mail(fake_account, msg_test, from_addr, to_addrs)) print(result) assert result["delivery_status"] == { "*****@*****.**": { "status": "DELIVERED", "smtp_info": ("250", "Delivered"), } }
async def sendmail(request, user_id, account): """ Send an email """ if account.name != user_id: raise Forbidden("You can't consult another person account.") data = request.json from_addr = mailutils.parse_email(account.address) all_addrs = [ mailutils.parse_email(a["address"]) for a in data["recipients"] ] tos = [ mailutils.parse_email(a["address"]) for a in data["recipients"] if a["type"] == "to" ] ccs = [ mailutils.parse_email(a["address"]) for a in data["recipients"] if a["type"] == "cc" ] attachments = data.get("attachments", []) msg = mailutils.make_msg(data["subject"], data["content"], from_addr, tos, ccs, attachments) result = await send_mail(account, msg, from_addr, all_addrs) return json(result)
async def sendmail(request, account): data = request.json mail_sender = MailSender() from_addr = mailutils.parse_email(account.address) all_addrs = [mailutils.parse_email(a['address']) for a in data['recipients']] tos = [mailutils.parse_email(a['address']) for a in data['recipients'] if a['type'] == 'to'] ccs = [mailutils.parse_email(a['address']) for a in data['recipients'] if a['type'] == 'cc'] attachments = data.get('attachments', []) msg = mailutils.make_msg(data['subject'], data['content'], from_addr, tos, ccs, attachments) # First we store it saved_msg = await storage.store_msg( msg, account=account, from_addr=from_addr, to_addrs=all_addrs, incoming=False ) # Then we send it delivery_status = await mail_sender.send( msg, from_addr=from_addr.addr_spec, to_addrs=[a.addr_spec for a in all_addrs] ) # TODO update delivery status in store # Finally we return it return json(saved_msg)
def test_resend_mail(loop, fake_account, msg_test, settings): loop.run_until_complete(storage.start()) from_addr = mailutils.parse_email("*****@*****.**") to_addrs = [ mailutils.parse_email("*****@*****.**"), mailutils.parse_email("*****@*****.**"), ] with mock.patch("byemail.smtp.MsgSender._relay_to") as smtp_send: f = asyncio.Future(loop=loop) f.set_result({ "*****@*****.**": ("250", "Delivered"), "*****@*****.**": ("534", "Fail for any reason"), }) smtp_send.return_value = f mail_to_resend = loop.run_until_complete( smtp.send_mail(fake_account, msg_test, from_addr, to_addrs)) assert mail_to_resend["delivery_status"] == { "*****@*****.**": { "status": "DELIVERED", "smtp_info": ("250", "Delivered"), }, "*****@*****.**": { "status": "ERROR", "reason": "SMTP_ERROR", "smtp_info": ("534", "Fail for any reason"), }, } with mock.patch("byemail.smtp.MsgSender._relay_to") as smtp_send: f = asyncio.Future(loop=loop) f.set_result({}) smtp_send.return_value = f mail_to_resend = loop.run_until_complete( smtp.resend_mail(fake_account, mail_to_resend, [to_addrs[1]])) # Does the delivery status update ? assert mail_to_resend["delivery_status"] == { "*****@*****.**": { "status": "DELIVERED", "smtp_info": ("250", "Delivered"), }, "*****@*****.**": { "status": "DELIVERED", "smtp_info": ("250", "Delivered") }, }
def dnsconfig(): """ Show configuration to apply to your DNS """ context = {} print("# This is the guessed configuration for your domain.") print("# Remember you should execute this command on the server where") print("# you start byemail.") for account in settings.ACCOUNTS: result = [] context["externalip"] = ( request.urlopen("https://api.ipify.org/").read().decode("utf8")) context["address_domain"] = mailutils.parse_email( account["address"]).domain context["dkim_selector"] = settings.DKIM_CONFIG["selector"] context["dkim_domain"] = settings.DKIM_CONFIG["domain"] """with open(settings.DKIM_CONFIG['public_key']) as key: context['publickey'] = key.read().replace('\n', '')\ .replace('-----BEGIN PUBLIC KEY-----', '')\ .replace('-----END PUBLIC KEY-----', '')""" result.append(MX_TPL.format(**context)) result.append(SPF_TPL.format(**context)) # result.append(DKIM_TPL.format(**context)) result.append(DMARC_TPL.format(**context)) print( f"\n# For account {account['name']}, domain {context['address_domain']}\n" ) print("\n".join(result)) print()
def dnsconfig(): context = {} print("# This is the guessed configuration for your domain.") print("# Remember you should execute this command on the server where") print("# you start byemail.") for account in settings.ACCOUNTS: result = [] context['externalip'] = request.urlopen( 'https://api.ipify.org/').read().decode('utf8') context['address_domain'] = mailutils.parse_email( account['address']).domain context['dkim_selector'] = settings.DKIM_CONFIG['selector'] context['dkim_domain'] = settings.DKIM_CONFIG['domain'] with open(settings.DKIM_CONFIG['public_key']) as key: context['publickey'] = key.read().replace('\n', '')\ .replace('-----BEGIN PUBLIC KEY-----', '')\ .replace('-----END PUBLIC KEY-----', '') result.append(MX_TPL.format(**context)) result.append(SPF_TPL.format(**context)) result.append(DKIM_TPL.format(**context)) result.append(DMARC_TPL.format(**context)) print("\n--- For account {name}, domain {dkim_domain}\n".format( **account, **context)) print('\n'.join(result)) print("\n---")
async def main(): settings.init_settings() await storage.storage.start() fake = Faker() for account_name, account in account_manager.accounts.items(): from_addr = mailutils.parse_email(account.address) print(f"Generate messages for {from_addr}...") for _ in range(randint(3, 5)): to_addr = mailutils.parse_email(fake.safe_email()) for _ in range(randint(3, 5)): # First message subject = fake.sentence(nb_words=5, variable_nb_words=True) msg = mailutils.make_msg(subject=subject, content=fake.text(max_nb_chars=200, ext_word_list=None), from_addr=from_addr, tos=[to_addr], ccs=None, attachments=None) msg_to_store = await mailutils.extract_data_from_msg(msg) saved_msg = await storage.storage.store_msg( msg_to_store, account=account, from_addr=from_addr, to_addrs=[to_addr], incoming=False) # response msg = mailutils.make_msg(subject="Re: " + subject, content=fake.text(max_nb_chars=200, ext_word_list=None), from_addr=to_addr, tos=[from_addr], ccs=None, attachments=None) msg_to_store = await mailutils.extract_data_from_msg(msg) saved_msg = await storage.storage.store_msg( msg_to_store, account=account, from_addr=to_addr, to_addrs=[from_addr], incoming=True) await storage.storage.stop()
def _test_msg_signing(): # Use https://www.mail-tester.com to test from_address = mailutils.parse_email( "Joe SixPack <*****@*****.**>") to_address = mailutils.parse_email("Suzie Q <*****@*****.**>") with mock.patch('byemail.mailutils.settings') as set_mock: set_mock.DKIM_CONFIG = { 'private_key': os.path.join(DATADIR, 'example_private.pem'), 'public_key': os.path.join(DATADIR, 'example_public.pem'), 'selector': 'test', 'domain': 'example.com', 'headers': ['From', 'To', 'Subject', 'Date', 'Message-Id'] } msg = mailutils.make_msg("Is dinner ready?", message_content, from_addr=from_address, tos=to_address) dkim_sign = mailutils.gen_dkim_sign(msg) #new_dkim = mailutils.gen_dkim_sign2(msg) #assert old_dkim == new_dkim, "DKIM differs" msg['DKIM-Signature'] = dkim_sign print(msg['DKIM-Signature']) with open(os.path.join(DATADIR, 'example_public.pem')) as key: publickey = key.read().replace('\n', '')\ .replace('-----BEGIN PUBLIC KEY-----', '')\ .replace('-----END PUBLIC KEY-----', '') def dnsfunc(*args): print("Called with ", *args) return DNS_DKIM_RESPONSE_TPL.format(publickey) assert dkim.verify(msg.as_string().encode(), dnsfunc=dnsfunc)
def test_make_msg(loop): """ Test message composition """ from_address = mailutils.parse_email( "Joe SixPack <*****@*****.**>") to_address = mailutils.parse_email("Suzie Q <*****@*****.**>") with mock.patch('byemail.mailutils.settings') as set_mock: set_mock.DKIM_CONFIG = { 'private_key': os.path.join(DATADIR, 'example_private.pem'), 'public_key': os.path.join(DATADIR, 'example_public.pem'), 'selector': 'test', 'domain': 'example.com', 'headers': ['From', 'To', 'Subject', 'Date', 'Message-Id'] } msg = mailutils.make_msg("Is dinner ready?", message_content, from_addr=from_address, tos=to_address) print(msg) assert msg['From'] == str(from_address)
async def populate_with_test_data(storage): """ Populate database with test data for testing purpose """ account = account_manager.get_account_for_address("*****@*****.**") print(account) for msg_src, from_addr in incoming_messages: msg = BytesParser(policy=policy.default).parsebytes(msg_src) recipients = [parse_email("Test <*****@*****.**>")] await storage.store_mail(account, msg, from_addr, recipients, incoming=True) for msg_src, recipients in outgoing_messages: from_addr = "*****@*****.**" msg = BytesParser(policy=policy.default).parsebytes(msg_src) await storage.store_mail(account, msg, from_addr, recipients, incoming=False)
async def mail_resend(request, user_id, mail_id, account): """ Resend an email by uid for selected recipient""" if account.name != user_id: raise Forbidden("You can't consult another person account.") mail_to_resend = await storage.get_mail(mail_id) # Check permissions if mail_to_resend["account"] != account.name: raise Forbidden("You don't have permission to resend this mail.") data = request.json tos = [mailutils.parse_email(data["to"])] result = await resend_mail(account, mail_to_resend, tos) return json(result)
OUTGOING_1 = ( b"""Content-Type: text/plain; charset="utf-8"\r Content-Transfer-Encoding: 7bit\r MIME-Version: 1.0\r From: Test <*****@*****.**>\r Subject: Second mail\r Message-Id: <152060134529.22888.2561159661807344298@emiter>\r To: Suzie <*****@*****.**>\r Date: Fri, 09 Mar 2019 14:16:45 +0100\r \r Yes sure, why not.\r \r Test.\r \r """, [parse_email("Suzie <*****@*****.**>")], ) incoming_messages = [INCOMING_1, INCOMING_2] outgoing_messages = [OUTGOING_1] async def populate_with_test_data(storage): """ Populate database with test data for testing purpose """ account = account_manager.get_account_for_address("*****@*****.**") print(account) for msg_src, from_addr in incoming_messages: msg = BytesParser(policy=policy.default).parsebytes(msg_src) recipients = [parse_email("Test <*****@*****.**>")]