summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJannis M. Hoffmann <jannis@fehcom.de>2024-04-04 12:13:29 +0200
committerJannis M. Hoffmann <jannis@fehcom.de>2024-04-04 12:13:29 +0200
commitf43673af60286ddf97c70035ecd99e817ff846f7 (patch)
treeb59d991daa81fe717b677f9960d3aa0df54350e2
parent65864b6d92800978de3e33cdfb9752f821c5dbf5 (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.toml2
-rwxr-xr-xscript/moveto3.py29
-rw-r--r--src/jwebmail/__init__.py2
-rw-r--r--src/jwebmail/model/read_mails.py58
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: