Initial status for developed files on dagda

This commit is contained in:
mailcat
2026-07-20 20:49:36 +02:00
commit 337378a362
102 changed files with 37778 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
"""Shared IMAP mailbox-name helpers."""
import base64
import re
def to_mutf7(s: str) -> str:
"""Encode a mailbox name as IMAP modified UTF-7."""
res = []
buf = []
for c in s:
if 0x20 <= ord(c) <= 0x7E and c != "&":
if buf:
b64 = (
base64.b64encode("".join(buf).encode("utf-16-be"))
.decode("ascii")
.rstrip("=")
.replace("/", ",")
)
res.append(f"&{b64}-")
buf.clear()
res.append(c)
elif c == "&":
if buf:
b64 = (
base64.b64encode("".join(buf).encode("utf-16-be"))
.decode("ascii")
.rstrip("=")
.replace("/", ",")
)
res.append(f"&{b64}-")
buf.clear()
res.append("&-")
else:
buf.append(c)
if buf:
b64 = (
base64.b64encode("".join(buf).encode("utf-16-be"))
.decode("ascii")
.rstrip("=")
.replace("/", ",")
)
res.append(f"&{b64}-")
return "".join(res)
def from_mutf7(s: str) -> str:
"""Decode an IMAP modified UTF-7 mailbox name."""
res = []
i = 0
while i < len(s):
if s[i] == "&":
j = s.index("-", i + 1)
if j == i + 1:
res.append("&")
else:
encoded = s[i + 1 : j].replace(",", "/")
pad = (4 - len(encoded) % 4) % 4
res.append(
base64.b64decode(encoded + "=" * pad).decode("utf-16-be")
)
i = j + 1
else:
res.append(s[i])
i += 1
return "".join(res)
def quote_mailbox(folder: str) -> str:
"""Return an IMAP-safe quoted mailbox name."""
escaped = to_mutf7(folder).replace("\\", "\\\\").replace('"', r"\"")
return f'"{escaped}"'
def parse_folder_name(raw: bytes) -> str | None:
"""Extract and decode the mailbox name from an IMAP LIST response."""
decoded = raw.decode("ascii", errors="replace")
match = re.match(r'\(.*?\)\s+".*?"\s+(.*)', decoded)
if not match:
return None
name = match.group(1).strip().strip('"')
if not name:
return None
try:
return from_mutf7(name)
except Exception:
return name
def list_folders(mail) -> list[str]:
"""Return decoded folder names from an IMAP connection."""
status, raw_folders = mail.list()
if status != "OK" or not raw_folders:
return []
folders = [parse_folder_name(item) for item in raw_folders if item]
return sorted(folder for folder in folders if folder)