aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHugo Hörnquist <hugo@lysator.liu.se>2022-12-01 05:10:14 +0100
committerHugo Hörnquist <hugo@lysator.liu.se>2022-12-01 05:24:01 +0100
commit9d50287dab79fb10a3a9a9d8dae39cbc3b045e52 (patch)
tree6891bb1d90bbceccbadd05924202834f6eb1aaa4
parentRemove debug prints. (diff)
downloadmu4web-9d50287dab79fb10a3a9a9d8dae39cbc3b045e52.tar.gz
mu4web-9d50287dab79fb10a3a9a9d8dae39cbc3b045e52.tar.xz
Fix scanning of maildirs.
-rw-r--r--mu4web/main.py107
-rw-r--r--setup.cfg1
2 files changed, 77 insertions, 31 deletions
diff --git a/mu4web/main.py b/mu4web/main.py
index 68bf144..4d39c1a 100644
--- a/mu4web/main.py
+++ b/mu4web/main.py
@@ -25,6 +25,7 @@ from user.local import LocalUser
from user.pam import PamUser
import subprocess
+from dataclasses import dataclass
import flask
from flask import (
@@ -37,6 +38,11 @@ from flask import (
get_flashed_messages
)
+try:
+ from natsort import natsorted
+except ModuleNotFoundError:
+ natsorted = sorted
+
#
# A few operations depend on the index of attachements. These index
# are all pre-order traversal indexes of the attachement tree, which
@@ -327,53 +333,92 @@ def search_page(q: str, by: Optional[mu.Sortfield],
body=main_body))
-def find_maildirs(basedir) -> dict[str, list[str]]:
+@dataclass
+class Leaf:
+ name: str
+
+@dataclass
+class Node:
+ name: str
+ children: list[Union[Leaf, 'Node']]
+
+
+def build_tree(items: list[list[str]]) -> Node:
+ groups: dict[str, list[list[str]]] = {}
+ direct: list[Leaf] = []
+ for key, *rest in items:
+ if rest:
+ groups.setdefault(key, []).append(rest)
+ else:
+ direct.append(Leaf(key))
+
+ node = Node('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(basedir, **flags) -> list[bytes]:
+ cmdline = ['find', basedir]
+ for key, value in flags.items():
+ cmdline += [f'-{key}', value]
+ cmdline.append('-print0')
+
+ cmd = subprocess.run(cmdline, capture_output=True)
+ return cmd.stdout.split(b'\0')[:-1]
+
+
+def find_maildirs(basedir) -> Node:
"""
Find all maildirs located under basedir.
A maildir is defined as any directory which contains a `cur`
directory.
-
- TODO Currently all returns are grouped by their first component, which
- fails for directories directly in the root.
"""
- cmd = subprocess.run(['find', basedir,
- '-type', 'd',
- '-name', 'cur',
- '-print0'],
- capture_output=True)
- groups: dict[str, list[str]] = {}
- # Group by first component
- for entry in cmd.stdout.split(b'\0'):
- dir = os.path.split(entry)[0][len(basedir) + 1:].decode('UTF-8')
- if not dir:
- continue
- parts = dir.split(os.path.sep)
- groups.setdefault(parts[0], []).append(os.path.sep.join(parts[1:]))
- return groups
+ 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: Node, 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, Leaf):
+ 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)
def index_page():
data = mu.info()
- groups = find_maildirs(data['maildir'])
-
- entries = []
-
- for key, values in sorted(groups.items(), key=lambda p: p[0]):
- entries.append(('li',
- ('details',
- ('summary', key),
- ('ul',
- [('li',
- ('a', {'href': 'search?' + urlencode({'q': f'maildir:"/{key}/{v}"'})}, v))
- for v in sorted(values)]))))
+ maildirs = find_maildirs(data['maildir'] + '/')
+
+ entries = serialize_maildir(maildirs)
+
rows = []
for key, value in data.items():
rows.append(('tr',
('td', key),
('td', value)))
body = [('div', ('table', ('tbody', rows))),
- ('div', ('ul', entries)),
+ ('div', entries),
]
return render_document(page_base(title='Mail index',
diff --git a/setup.cfg b/setup.cfg
index bf73722..aba19bf 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -15,6 +15,7 @@ install_requires =
flask >= 2.2.2
flask-login >= 0.6
urllib3 >= 1.26
+# optionally natsort >= 8.2
setup_requires =
setuptools
py_modules = mu4web