blob: 8508d2264c4f3d8587211031364ae6cb66bc41bf (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#!/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 email.parser
import email.policy
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.
parser = email.parser.BytesParser(policy=email.policy.default)
msg = parser.parse(sys.stdin.buffer)
# Handle mailman message mangling used to work around DMARC rejection rules:
#
# - mailman may encapsulate original messages in a message/rfc822 part.
# That's an easy case, simply extract the encapsulated message. Any
# additional mangling of the outer message (such as From header mangling)
# can be ignored.
#
# - mailman may mangle the From header. 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.
if msg.get_content_type() == 'message/rfc822':
msg = next(msg.iter_parts())
else:
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))
|