diff options
author | Jannis M. Hoffmann <jannis@fehcom.de> | 2024-04-04 12:13:29 +0200 |
---|---|---|
committer | Jannis M. Hoffmann <jannis@fehcom.de> | 2024-04-04 12:13:29 +0200 |
commit | f43673af60286ddf97c70035ecd99e817ff846f7 (patch) | |
tree | b59d991daa81fe717b677f9960d3aa0df54350e2 | |
parent | 65864b6d92800978de3e33cdfb9752f821c5dbf5 (diff) |
introduce moveto3
A program to move a file descriptor to 3 required for qmail-authuser and does not require a shell
-rw-r--r-- | pyproject.toml | 2 | ||||
-rwxr-xr-x | script/moveto3.py | 29 | ||||
-rw-r--r-- | src/jwebmail/__init__.py | 2 | ||||
-rw-r--r-- | src/jwebmail/model/read_mails.py | 58 |
4 files changed, 59 insertions, 32 deletions
diff --git a/pyproject.toml b/pyproject.toml index cb9caf6..8e5b337 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ ] [tool.hatch.envs.dev.scripts] -serve = "flask --app src/jwebmail --debug run --extra-files src/jwebmail/translations" +serve = 'env PATH="$PATH:$PWD/script/" flask --app src/jwebmail --debug run --extra-files src/jwebmail/translations' server = "env JWEBMAIL_CONFIG=../../jwebmail.toml flask --app src/jwebmail run" tr-compile = "pybabel compile -d src/jwebmail/translations/" tr-extract = "pybabel extract -F babel.cfg -o messages.pot -k lazy_gettext src/ && pybabel update -i messages.pot -d src/jwebmail/translations/" diff --git a/script/moveto3.py b/script/moveto3.py new file mode 100755 index 0000000..ae10d63 --- /dev/null +++ b/script/moveto3.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +import argparse +import os +import sys + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("-a", default="qmail-authuser", dest="pam") + ap.add_argument("fd", type=int) + ap.add_argument("prog") + ap.add_argument("args", nargs='*') + + vals = ap.parse_args() + + if vals.fd < 3: + raise ValueError(f"fd({fd}) must be 3 or greater") + + if vals.fd != 3: + os.dup2(vals.fd, 3) + os.close(vals.fd) + + os.execvp(vals.pam, [vals.pam, vals.prog] + vals.args) + + raise ValueError("should not be reachable") + + +if __name__ == '__main__': + main() diff --git a/src/jwebmail/__init__.py b/src/jwebmail/__init__.py index 9663647..278ef50 100644 --- a/src/jwebmail/__init__.py +++ b/src/jwebmail/__init__.py @@ -34,7 +34,7 @@ else: toml_read_file = dict(load=toml_load, text=True) -__version__ = "2.2.0.dev4" +__version__ = "2.2.1.dev0" def validate_config(app): diff --git a/src/jwebmail/model/read_mails.py b/src/jwebmail/model/read_mails.py index 43f13ca..d3887ca 100644 --- a/src/jwebmail/model/read_mails.py +++ b/src/jwebmail/model/read_mails.py @@ -1,4 +1,3 @@ -import shlex import os from subprocess import PIPE, Popen, TimeoutExpired from subprocess import run as subprocess_run @@ -202,43 +201,42 @@ class QMailAuthuser: idx = user_mail_addr.find("@") user_name = user_mail_addr[:idx] - cmdline = " ".join( - shlex.quote(str(x)) - for x in ( - self._authenticator, - self._prog, - self._mailbox_path, - self._virtual_user, - user_name, - mode, - ) - ) - - if rp != 3: - cmdline += f" 3<&{rp} {rp}<&-" + cmdline = [ + "moveto3.py", + "-a", + self._authenticator, + str(rp), + self._prog, + self._mailbox_path, + self._virtual_user, + user_name, + mode, + ] return cmdline def _read_qmauth(self, cmd, args, rp, wp): - popen = Popen(cmd, stdin=PIPE, stdout=PIPE, pass_fds=[rp], shell=True, bufsize=0) + with Popen(cmd, stdin=PIPE, stdout=PIPE, pass_fds=[rp], bufsize=0) as popen: + os.close(rp) + os.write(wp, f"{self._username}\0{self._password}\0\0".encode()) + os.close(wp) + r = popen.stdout.read(10) + if popen.poll(): + raise QMAuthError( + "qmail-authuser unexpectedly exited", popen.returncode, r + ) + assert r == b"OPEN\n" + popen.stdin.write(args) + popen.stdin.close() + inp = popen.stdout.readall() - os.close(rp) - os.write(wp, f"{self._username}\0{self._password}\0\0".encode()) - os.close(wp) - r = popen.stdout.read(10) - if popen.poll(): - raise QMAuthError("qmail-authuser unexpectedly exited", popen.returncode, r) - assert r == b"OPEN\n" - popen.stdin.write(args) - popen.stdin.close() - inp = popen.stdout.readall() + if popen.poll() is None: + popen.kill() + popen.poll() - if popen.poll() is None: - popen.kill() - popen.poll() + rc = popen.returncode - rc = popen.returncode if rc == 0: return inp elif rc == 3: |