summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2022-11-28 07:59:26 +0200
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2022-11-28 08:27:13 +0200
commit48df8095d4e50a3c98a14939ed046f2e100615f7 (patch)
tree040abf8e56f43d79c7d96b43072290048d0845b1
parent39bf2ecc06663ac859290ca3b3a05fd775246766 (diff)
Add patch-save.py script
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-rwxr-xr-xpatch-save.py90
1 files changed, 90 insertions, 0 deletions
diff --git a/patch-save.py b/patch-save.py
new file mode 100755
index 0000000..1c0516f
--- /dev/null
+++ b/patch-save.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2022 Laurent Pinchart
+#
+# Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+#
+# Patch save script for mutt that handles mailman From header mangling.
+#
+# To install, copy the script to ~/.mutt/, and add the following lines to your
+# .muttrc to bind to ctrl-h.
+#
+# set pipe_split=yes
+# macro index \Ch "<enter-command>unset wait_key<enter>
+# <enter-command>set pipe_decode<enter>
+# <tag-prefix><pipe-entry>~/.mutt/patch-save.py<enter>
+# <enter-command>unset pipe_decode<enter>
+# <enter-command>set wait_key<enter>" "output git patches"
+#
+
+import email
+import pathlib
+import re
+import sys
+
+
+def sanitize(s):
+ out = ''
+
+ for c in s:
+ if c in '[]':
+ continue
+
+ if c in "'":
+ c = '.'
+ elif c in '/:':
+ c = '-'
+ elif c in '*()" \t\n\r':
+ c = '_'
+
+ out += c
+
+ return out
+
+
+def main(argv):
+
+ # Parse the message from stdin as binary, as we can't assume any particular
+ # encoding.
+ msg = email.message_from_binary_file(sys.stdin.buffer)
+
+ # mailman can mangle the From header to work around DMARC rejection rules.
+ # It then formats From as "Name via mailing-list <mailing-list@domain>" and
+ # stores the original sender in the Reply-to header. Restore the original
+ # author in the From header to please git-am.
+ from_header = msg.get('From')
+ reply_to = msg.get('Reply-to')
+ if reply_to and ' via ' in from_header:
+ msg.replace_header('From', reply_to)
+
+ subject = msg.get('Subject')
+ if not subject:
+ return 1
+
+ # The subject can be an str instance or an email.header.Header instance.
+ # Convert it to str in all cases.
+ subject = str(subject)
+
+ # Strip everything before the [PATCH] or [RESEND] tag.
+ match = re.search(r'\[(PATCH|RESEND) [^]]*\].*', subject)
+ if match:
+ subject = match.group(0)
+
+ # Sanitize the subject.
+ subject = sanitize(subject)
+
+ subject = subject.replace('..', '.')
+ subject = subject.replace('-_', '_')
+ subject = subject.replace('__', '_')
+
+ subject = subject.strip()
+ subject = subject.rstrip('.')
+
+ file_name = pathlib.Path.home() / '.maildir' / 'patches' / (subject + '.patch')
+ open(file_name, 'wb').write(bytes(msg))
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))