From ed18b0c89c49ff59c17a83dbb200a34f8ac38ec4 Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Mon, 1 Apr 2024 11:11:07 -0400 Subject: [PATCH 1/3] wip --- mail4one/pop3.py | 9 +++++++-- mail4one/poputils.py | 7 +++++++ tests/test_pop.py | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mail4one/pop3.py b/mail4one/pop3.py index 3214ce9..d3de548 100644 --- a/mail4one/pop3.py +++ b/mail4one/pop3.py @@ -29,7 +29,7 @@ from .poputils import ( end, Request, MailEntry, - get_mail, + get_mail_fp, get_mails_list, MailList, ) @@ -217,7 +217,12 @@ def trans_command_retr(mails: MailList, req: Request) -> None: entry = mails.get(req.arg1) if entry: write(ok("Contents follow")) - write(get_mail(entry)) + with get_mail_fp(entry) as fp: + for line in fp: + if line[0] == b'.': + write(b'.') + write(line) + # write(get_mail(entry)) write(end()) mails.delete(req.arg1) else: diff --git a/mail4one/poputils.py b/mail4one/poputils.py index c325ff1..451a1e2 100644 --- a/mail4one/poputils.py +++ b/mail4one/poputils.py @@ -2,6 +2,7 @@ import os from dataclasses import dataclass from enum import Enum, auto from pathlib import Path +from contextlib import contextmanager class ClientError(Exception): @@ -124,6 +125,12 @@ def set_nid(entries: list[MailEntry]): entry.nid = i +@contextmanager +def get_mail_fp(entry: MailEntry): + with open(entry.path, mode="rb") as fp: + yield fp + + def get_mail(entry: MailEntry) -> bytes: with open(entry.path, mode="rb") as fp: return fp.read() diff --git a/tests/test_pop.py b/tests/test_pop.py index 93fd879..b17dfd1 100644 --- a/tests/test_pop.py +++ b/tests/test_pop.py @@ -21,7 +21,13 @@ AQXWXSWNGAEPGIMG2F3QDKBXL3MRHY6K2BPID64ZR6LABLPVSF TEST_USER = "foobar" TEST_MBOX = "foobar_mails" -USERS = [User(username=TEST_USER, password_hash=TEST_HASH, mbox=TEST_MBOX)] +TEST_USER2 = "foo2" +TEST_MBOX2 = "foo2mails" + +USERS = [ + User(username=TEST_USER, password_hash=TEST_HASH, mbox=TEST_MBOX), + User(username=TEST_USER2, password_hash=TEST_HASH, mbox=TEST_MBOX2), +] MAILS_PATH: Path -- 2.34.1 From d87025002a9efb2e2529642b622d529a1d6eda44 Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Mon, 1 Apr 2024 17:41:40 -0400 Subject: [PATCH 2/3] add tests for dot stuff --- mail4one/pop3.py | 13 +++++++------ tests/test_pop.py | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/mail4one/pop3.py b/mail4one/pop3.py index d3de548..9600de8 100644 --- a/mail4one/pop3.py +++ b/mail4one/pop3.py @@ -219,10 +219,10 @@ def trans_command_retr(mails: MailList, req: Request) -> None: write(ok("Contents follow")) with get_mail_fp(entry) as fp: for line in fp: - if line[0] == b'.': - write(b'.') + if line.startswith(b'.'): + write(b'.') # prepend dot write(line) - # write(get_mail(entry)) + # write(get_mail(entry)) # no prepend dot write(end()) mails.delete(req.arg1) else: @@ -391,13 +391,14 @@ def debug_main(): logging.basicConfig(level=logging.DEBUG) import sys + from .pwhash import gen_pwhash - _, mails_path, port, password = sys.argv + _, mails_path, mbox = sys.argv mails_path = Path(mails_path) - port = int(port) + users = [ User(username="dummy", password_hash=gen_pwhash("dummy"), mbox=mbox) ] - asyncio.run(a_main(mails_path, port, password_hash=password_hash)) + asyncio.run(a_main("127.0.0.1", 1101, mails_path, users=users)) if __name__ == "__main__": diff --git a/tests/test_pop.py b/tests/test_pop.py index b17dfd1..f5def85 100644 --- a/tests/test_pop.py +++ b/tests/test_pop.py @@ -4,18 +4,17 @@ import logging import tempfile import time import os +import poplib from mail4one.pop3 import create_pop_server from mail4one.config import User from pathlib import Path TEST_HASH = "".join( - c - for c in """ + """ AFTY5EVN7AX47ZL7UMH3BETYWFBTAV3XHR73CEFAJBPN2NIHPWD ZHV2UQSMSPHSQQ2A2BFQBNC77VL7F2UKATQNJZGYLCSU6C43UQD AQXWXSWNGAEPGIMG2F3QDKBXL3MRHY6K2BPID64ZR6LABLPVSF -""" - if not c.isspace() +""".split() ) TEST_USER = "foobar" @@ -48,7 +47,8 @@ Hello bro\r IlzVOJqu9Zp7twFAtzcV\r yQVk36B0mGU2gtWxXLr\r PeF0RtbI0mAuVPLQDHCi\r -\r\n""" +\r +""" def setUpModule() -> None: @@ -57,13 +57,21 @@ def setUpModule() -> None: td = tempfile.TemporaryDirectory(prefix="m41.pop.") unittest.addModuleCleanup(td.cleanup) MAILS_PATH = Path(td.name) - os.mkdir(MAILS_PATH / TEST_MBOX) - for md in ("new", "cur", "tmp"): - os.mkdir(MAILS_PATH / TEST_MBOX / md) + for mbox in (TEST_MBOX, TEST_MBOX2): + os.mkdir(MAILS_PATH / mbox) + for md in ("new", "cur", "tmp"): + os.mkdir(MAILS_PATH / mbox / md) with open(MAILS_PATH / TEST_MBOX / "new/msg1.eml", "wb") as f: f.write(TESTMAIL) with open(MAILS_PATH / TEST_MBOX / "new/msg2.eml", "wb") as f: f.write(TESTMAIL) + with open(MAILS_PATH / TEST_MBOX2 / "new/msg1.eml", "wb") as f: + f.write(TESTMAIL) + f.write(b"More lines to follow\r\n") + f.write(b".Line starts with a dot\r\n") + f.write(b"some more lines\r\n") + f.write(b".\r\n") + f.write(b"Previous line just has a dot\r\n") logging.debug(MAILS_PATH) @@ -205,6 +213,21 @@ class TestPop3(unittest.IsolatedAsyncioTestCase): """ await self.dialog_checker(dialog) + async def test_poplib(self) -> None: + def run_poplib(): + pc = poplib.POP3("127.0.0.1", 7995) + try: + self.assertEqual(b"+OK Server Ready", pc.getwelcome()) + self.assertEqual(b'+OK Welcome', pc.user("foo2")) + self.assertEqual(b'+OK Login successful', pc.pass_("helloworld")) + _, eml, oc = pc.retr(1) + self.assertIn(b"Previous line just has a dot", eml) + self.assertIn(b".Line starts with a dot", eml) + self.assertIn(b".", eml) + finally: + pc.quit() + await asyncio.to_thread(run_poplib) + async def asyncTearDown(self) -> None: logging.debug("at teardown") self.writer.close() -- 2.34.1 From ed8ff2728f0832509a5a7e9b1637261d00a8e5bf Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Mon, 1 Apr 2024 17:42:48 -0400 Subject: [PATCH 3/3] format --- mail4one/pop3.py | 8 ++++---- tests/test_pop.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mail4one/pop3.py b/mail4one/pop3.py index 9600de8..f6bde47 100644 --- a/mail4one/pop3.py +++ b/mail4one/pop3.py @@ -219,8 +219,8 @@ def trans_command_retr(mails: MailList, req: Request) -> None: write(ok("Contents follow")) with get_mail_fp(entry) as fp: for line in fp: - if line.startswith(b'.'): - write(b'.') # prepend dot + if line.startswith(b"."): + write(b".") # prepend dot write(line) # write(get_mail(entry)) # no prepend dot write(end()) @@ -396,9 +396,9 @@ def debug_main(): _, mails_path, mbox = sys.argv mails_path = Path(mails_path) - users = [ User(username="dummy", password_hash=gen_pwhash("dummy"), mbox=mbox) ] + users = [User(username="dummy", password_hash=gen_pwhash("dummy"), mbox=mbox)] - asyncio.run(a_main("127.0.0.1", 1101, mails_path, users=users)) + asyncio.run(a_main("127.0.0.1", 1101, mails_path, users=users)) if __name__ == "__main__": diff --git a/tests/test_pop.py b/tests/test_pop.py index f5def85..1e17d38 100644 --- a/tests/test_pop.py +++ b/tests/test_pop.py @@ -218,14 +218,15 @@ class TestPop3(unittest.IsolatedAsyncioTestCase): pc = poplib.POP3("127.0.0.1", 7995) try: self.assertEqual(b"+OK Server Ready", pc.getwelcome()) - self.assertEqual(b'+OK Welcome', pc.user("foo2")) - self.assertEqual(b'+OK Login successful', pc.pass_("helloworld")) + self.assertEqual(b"+OK Welcome", pc.user("foo2")) + self.assertEqual(b"+OK Login successful", pc.pass_("helloworld")) _, eml, oc = pc.retr(1) self.assertIn(b"Previous line just has a dot", eml) self.assertIn(b".Line starts with a dot", eml) self.assertIn(b".", eml) finally: pc.quit() + await asyncio.to_thread(run_poplib) async def asyncTearDown(self) -> None: -- 2.34.1