pop3 should byte-stuff lines starting with dot #5

Merged
balki merged 4 commits from popdotstuff into main 2024-04-01 17:51:23 -04:00
2 changed files with 38 additions and 14 deletions
Showing only changes of commit d87025002a - Show all commits

View File

@ -219,10 +219,10 @@ def trans_command_retr(mails: MailList, req: Request) -> None:
write(ok("Contents follow")) write(ok("Contents follow"))
with get_mail_fp(entry) as fp: with get_mail_fp(entry) as fp:
for line in fp: for line in fp:
if line[0] == b'.': if line.startswith(b'.'):
write(b'.') write(b'.') # prepend dot
write(line) write(line)
# write(get_mail(entry)) # write(get_mail(entry)) # no prepend dot
write(end()) write(end())
mails.delete(req.arg1) mails.delete(req.arg1)
else: else:
@ -391,13 +391,14 @@ def debug_main():
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
import sys import sys
from .pwhash import gen_pwhash
_, mails_path, port, password = sys.argv _, mails_path, mbox = sys.argv
mails_path = Path(mails_path) 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__": if __name__ == "__main__":

View File

@ -4,18 +4,17 @@ import logging
import tempfile import tempfile
import time import time
import os import os
import poplib
from mail4one.pop3 import create_pop_server from mail4one.pop3 import create_pop_server
from mail4one.config import User from mail4one.config import User
from pathlib import Path from pathlib import Path
TEST_HASH = "".join( TEST_HASH = "".join(
c """
for c in """
AFTY5EVN7AX47ZL7UMH3BETYWFBTAV3XHR73CEFAJBPN2NIHPWD AFTY5EVN7AX47ZL7UMH3BETYWFBTAV3XHR73CEFAJBPN2NIHPWD
ZHV2UQSMSPHSQQ2A2BFQBNC77VL7F2UKATQNJZGYLCSU6C43UQD ZHV2UQSMSPHSQQ2A2BFQBNC77VL7F2UKATQNJZGYLCSU6C43UQD
AQXWXSWNGAEPGIMG2F3QDKBXL3MRHY6K2BPID64ZR6LABLPVSF AQXWXSWNGAEPGIMG2F3QDKBXL3MRHY6K2BPID64ZR6LABLPVSF
""" """.split()
if not c.isspace()
) )
TEST_USER = "foobar" TEST_USER = "foobar"
@ -48,7 +47,8 @@ Hello bro\r
IlzVOJqu9Zp7twFAtzcV\r IlzVOJqu9Zp7twFAtzcV\r
yQVk36B0mGU2gtWxXLr\r yQVk36B0mGU2gtWxXLr\r
PeF0RtbI0mAuVPLQDHCi\r PeF0RtbI0mAuVPLQDHCi\r
\r\n""" \r
"""
def setUpModule() -> None: def setUpModule() -> None:
@ -57,13 +57,21 @@ def setUpModule() -> None:
td = tempfile.TemporaryDirectory(prefix="m41.pop.") td = tempfile.TemporaryDirectory(prefix="m41.pop.")
unittest.addModuleCleanup(td.cleanup) unittest.addModuleCleanup(td.cleanup)
MAILS_PATH = Path(td.name) MAILS_PATH = Path(td.name)
os.mkdir(MAILS_PATH / TEST_MBOX) for mbox in (TEST_MBOX, TEST_MBOX2):
for md in ("new", "cur", "tmp"): os.mkdir(MAILS_PATH / mbox)
os.mkdir(MAILS_PATH / TEST_MBOX / md) for md in ("new", "cur", "tmp"):
os.mkdir(MAILS_PATH / mbox / md)
with open(MAILS_PATH / TEST_MBOX / "new/msg1.eml", "wb") as f: with open(MAILS_PATH / TEST_MBOX / "new/msg1.eml", "wb") as f:
f.write(TESTMAIL) f.write(TESTMAIL)
with open(MAILS_PATH / TEST_MBOX / "new/msg2.eml", "wb") as f: with open(MAILS_PATH / TEST_MBOX / "new/msg2.eml", "wb") as f:
f.write(TESTMAIL) 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) logging.debug(MAILS_PATH)
@ -205,6 +213,21 @@ class TestPop3(unittest.IsolatedAsyncioTestCase):
""" """
await self.dialog_checker(dialog) 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: async def asyncTearDown(self) -> None:
logging.debug("at teardown") logging.debug("at teardown")
self.writer.close() self.writer.close()