aboutsummaryrefslogtreecommitdiff
path: root/mu4web/maildir.py
diff options
context:
space:
mode:
Diffstat (limited to 'mu4web/maildir.py')
-rw-r--r--mu4web/maildir.py88
1 files changed, 88 insertions, 0 deletions
diff --git a/mu4web/maildir.py b/mu4web/maildir.py
new file mode 100644
index 0000000..831f23d
--- /dev/null
+++ b/mu4web/maildir.py
@@ -0,0 +1,88 @@
+from dataclasses import dataclass
+import os.path
+
+from html_render import HTML
+from util import find
+
+from urllib.parse import urlencode
+
+from typing import (
+ Union
+)
+
+try:
+ from natsort import natsorted
+except ModuleNotFoundError:
+ natsorted = sorted
+
+@dataclass
+class MaildirEntry:
+ """A single maildir, used by find_maildirs."""
+ name: str
+
+
+@dataclass
+class MaildirGroup:
+ """A group of maildir, which isn't a maildir in itself."""
+ name: str
+ children: list[Union[MaildirEntry, 'MaildirGroup']]
+
+
+def _build_tree(items: list[list[str]]) -> MaildirGroup:
+ groups: dict[str, list[list[str]]] = {}
+ direct: list[MaildirEntry] = []
+ for key, *rest in items:
+ if rest:
+ groups.setdefault(key, []).append(rest)
+ else:
+ direct.append(MaildirEntry(key))
+
+ node = MaildirGroup('root', [])
+ for key, values in groups.items():
+ next = _build_tree(values)
+ next.name = key
+ node.children.append(next)
+ node.children.extend(direct)
+ return node
+
+
+
+def find_maildirs(basedir) -> MaildirGroup:
+ """
+ Find all maildirs located under basedir.
+
+ A maildir is defined as any directory which contains a `cur`
+ directory.
+
+ Returns a MaildirGroup of the root, whose children will be all the
+ maildirs under basedir. Note that if a directory is both a maildir
+ and a group of maildirs then it will have two separate entries.
+ """
+ basedir = basedir.rstrip('/')
+ files = find(basedir, type='d', name='cur')
+ # + 1 removes leading slash
+ # - 4 removes '/cur'
+ dirs = [entry[len(basedir)+1:-4].decode('UTF-8').split(os.path.sep)
+ for entry in files]
+
+ return _build_tree(dirs)
+
+
+def serialize_maildir(maildir: MaildirGroup, path: list[str] = []) -> HTML:
+ """Build a (recursive) list from a maildir node."""
+ entries: list[HTML] = []
+ for node in natsorted(maildir.children, key=lambda n: n.name):
+ if isinstance(node, MaildirEntry):
+ parts = '/'.join(path + [node.name])
+ url = 'search?' + urlencode({'q': f'maildir:"/{parts}"'})
+ entry = ('li', ('a', {'href': url},
+ node.name or ('i', 'root')))
+ else:
+ entry = ('li',
+ ('details',
+ ('summary', node.name),
+ serialize_maildir(node, path + [node.name])))
+ entries.append(entry)
+
+ return ('ul', *entries)
+