From 7b30ef1ee29aa33a9436a6108add5f5ef100f8da Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 16 Sep 2019 15:45:43 -0700 Subject: changelog: notmuch project shipped but did not install the script Reported-by: David Bremner Signed-off-by: Sean Whitton --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index aa8c371..ecf6ce2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,7 @@ mailscripts (0.11-1) unstable; urgency=medium * New script: email-print-mime-structure (Closes: #939993). - Imported from the notmuch project, which never shipped it in releases. + Imported from the notmuch project, which never installed it. Thanks to Daniel Kahn Gillmor for the patches. * Generate nroff output in UTF-8. Thanks to Daniel Kahn Gillmor for the patch. -- cgit v1.2.3 From 65fcb89b4d774d02ccafea735737a106ba05f295 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 1 Nov 2019 14:13:27 -0400 Subject: email-print-mime-structure: be typesafe This adds enough typechecking that the following check passes: mypy --strict email-print-mimestructure Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 54 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 7adeb2b..185173f 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -29,44 +29,49 @@ Example: If you want to number the parts, i suggest piping the output through something like "cat -n" ''' -import email import sys +import email +import logging -def print_part(z, prefix): - fname = '' if z.get_filename() is None else ' [' + z.get_filename() + ']' - cset = '' if z.get_charset() is None else ' (' + z.get_charset() + ')' - disp = z.get_params(None, header='Content-Disposition') - if (disp is None): - disposition = '' - else: - disposition = '' +from typing import Optional, Union, List, Tuple, Any +from email.charset import Charset +from email.message import Message + +def print_part(z:Message, prefix:str) -> None: + ofname:Optional[str] = z.get_filename() + fname:str = '' if ofname is None else f' [{ofname}]' + ocharset:Union[Charset, str, None] = z.get_charset() + cset:str = '' if ocharset is None else f' ({ocharset})' + disp:Union[List[Tuple[str,str]], List[str], None] = z.get_params(None, header='Content-Disposition') + disposition:str = '' + if (disp is not None): for d in disp: if d[0] in [ 'attachment', 'inline' ]: disposition = ' ' + d[0] + nbytes:int if z.is_multipart(): + # FIXME: it looks like we are counting chars here, not bytes: nbytes = len(z.as_string()) else: - nbytes = len(z.get_payload()) + payload:Union[List[Message], str, bytes, None] = z.get_payload() + if not isinstance(payload, (str,bytes)): + raise TypeError(f'expected payload to be either str or bytes, got {type(payload)}') + nbytes = len(payload) - print('{}{}{}{}{} {:d} bytes'.format( - prefix, - z.get_content_type(), - cset, - disposition, - fname, - nbytes, - )) + print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes} bytes') -def test(z, prefix=''): +def test(z:Message, prefix:str='') -> None: if (z.is_multipart()): print_part(z, prefix+'┬╴') if prefix.endswith('└'): prefix = prefix.rpartition('└')[0] + ' ' if prefix.endswith('├'): prefix = prefix.rpartition('├')[0] + '│' - parts = z.get_payload() + parts:Union[List[Message], str, bytes, None] = z.get_payload() + if not isinstance(parts, list): + raise TypeError(f'parts was {type(parts)}, expected List[Message]') i = 0 - while (i < parts.__len__()-1): + while (i < len(parts)-1): test(parts[i], prefix + '├') i += 1 test(parts[i], prefix + '└') @@ -74,4 +79,9 @@ def test(z, prefix=''): else: print_part(z, prefix+'─╴') -test(email.message_from_file(sys.stdin), '└') +msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) + +if isinstance(msg, Message): + test(msg, '└') +else: + logging.error('Input was not an e-mail message') -- cgit v1.2.3 From 8fde04b7d2743e393e93fd0677ef2fcfc53b351b Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 1 Nov 2019 20:38:29 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index ecf6ce2..4de2082 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +mailscripts (0.12-1) UNRELEASED; urgency=medium + + * email-print-mime-structure: make typesafe. + Thanks to Daniel Kahn Gillmor for the patch. + + -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 + mailscripts (0.11-1) unstable; urgency=medium * New script: email-print-mime-structure (Closes: #939993). -- cgit v1.2.3 From 75dbd9eb55cae90d6c962e9eb914ffa05d05d69e Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:17 -0400 Subject: email-print-mime-structure: refactor to a class We will need to send arguments to the printer, so it's handy to wrap the functionality in a class. No functional changes. This diff is probably best reviewed with whitespace changes ignored. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 82 ++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 185173f..b78ae91 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -37,51 +37,53 @@ from typing import Optional, Union, List, Tuple, Any from email.charset import Charset from email.message import Message -def print_part(z:Message, prefix:str) -> None: - ofname:Optional[str] = z.get_filename() - fname:str = '' if ofname is None else f' [{ofname}]' - ocharset:Union[Charset, str, None] = z.get_charset() - cset:str = '' if ocharset is None else f' ({ocharset})' - disp:Union[List[Tuple[str,str]], List[str], None] = z.get_params(None, header='Content-Disposition') - disposition:str = '' - if (disp is not None): - for d in disp: - if d[0] in [ 'attachment', 'inline' ]: - disposition = ' ' + d[0] - nbytes:int - if z.is_multipart(): - # FIXME: it looks like we are counting chars here, not bytes: - nbytes = len(z.as_string()) - else: - payload:Union[List[Message], str, bytes, None] = z.get_payload() - if not isinstance(payload, (str,bytes)): - raise TypeError(f'expected payload to be either str or bytes, got {type(payload)}') - nbytes = len(payload) +class MimePrinter(object): + def print_part(self, z:Message, prefix:str) -> None: + ofname:Optional[str] = z.get_filename() + fname:str = '' if ofname is None else f' [{ofname}]' + ocharset:Union[Charset, str, None] = z.get_charset() + cset:str = '' if ocharset is None else f' ({ocharset})' + disp:Union[List[Tuple[str,str]], List[str], None] = z.get_params(None, header='Content-Disposition') + disposition:str = '' + if (disp is not None): + for d in disp: + if d[0] in [ 'attachment', 'inline' ]: + disposition = ' ' + d[0] + nbytes:int + if z.is_multipart(): + # FIXME: it looks like we are counting chars here, not bytes: + nbytes = len(z.as_string()) + else: + payload:Union[List[Message], str, bytes, None] = z.get_payload() + if not isinstance(payload, (str,bytes)): + raise TypeError(f'expected payload to be either str or bytes, got {type(payload)}') + nbytes = len(payload) - print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes} bytes') + print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes} bytes') -def test(z:Message, prefix:str='') -> None: - if (z.is_multipart()): - print_part(z, prefix+'┬╴') - if prefix.endswith('└'): - prefix = prefix.rpartition('└')[0] + ' ' - if prefix.endswith('├'): - prefix = prefix.rpartition('├')[0] + '│' - parts:Union[List[Message], str, bytes, None] = z.get_payload() - if not isinstance(parts, list): - raise TypeError(f'parts was {type(parts)}, expected List[Message]') - i = 0 - while (i < len(parts)-1): - test(parts[i], prefix + '├') - i += 1 - test(parts[i], prefix + '└') - # FIXME: show epilogue? - else: - print_part(z, prefix+'─╴') + def test(self, z:Message, prefix:str='') -> None: + if (z.is_multipart()): + self.print_part(z, prefix+'┬╴') + if prefix.endswith('└'): + prefix = prefix.rpartition('└')[0] + ' ' + if prefix.endswith('├'): + prefix = prefix.rpartition('├')[0] + '│' + parts:Union[List[Message], str, bytes, None] = z.get_payload() + if not isinstance(parts, list): + raise TypeError(f'parts was {type(parts)}, expected List[Message]') + i = 0 + while (i < len(parts)-1): + self.test(parts[i], prefix + '├') + i += 1 + self.test(parts[i], prefix + '└') + # FIXME: show epilogue? + else: + self.print_part(z, prefix+'─╴') msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) if isinstance(msg, Message): - test(msg, '└') + printer:MimePrinter = MimePrinter() + printer.test(msg, '└') else: logging.error('Input was not an e-mail message') -- cgit v1.2.3 From a9a3a085c2f407f1dac144eba58087a7ebcf4e35 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:18 -0400 Subject: email-print-mime-structure: put main() into its own function No functional changes. This is a refactoring commit to provide some non-global scoping and easier readability. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index b78ae91..5eb94e0 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -80,10 +80,14 @@ class MimePrinter(object): else: self.print_part(z, prefix+'─╴') -msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) +def main() -> None: + msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) -if isinstance(msg, Message): - printer:MimePrinter = MimePrinter() - printer.test(msg, '└') -else: - logging.error('Input was not an e-mail message') + if isinstance(msg, Message): + printer:MimePrinter = MimePrinter() + printer.test(msg, '└') + else: + logging.error('Input was not an e-mail message') + +if __name__ == '__main__': + main() -- cgit v1.2.3 From 38e7f88f670589e1ffd61083a0f551369ba96d98 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:19 -0400 Subject: email-print-mime-structure: parse argments This adds a -h and --help option, which is currently pretty useless. But the argparse will become useful shortly. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 9 ++++++++- email-print-mime-structure.1.pod | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 5eb94e0..38dc8d9 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -33,11 +33,15 @@ import sys import email import logging +from argparse import ArgumentParser, Namespace from typing import Optional, Union, List, Tuple, Any from email.charset import Charset from email.message import Message class MimePrinter(object): + def __init__(self, args:Namespace): + self.args = args + def print_part(self, z:Message, prefix:str) -> None: ofname:Optional[str] = z.get_filename() fname:str = '' if ofname is None else f' [{ofname}]' @@ -81,10 +85,13 @@ class MimePrinter(object): self.print_part(z, prefix+'─╴') def main() -> None: + parser:ArgumentParser = ArgumentParser(description='Read RFC2822 MIME message from stdin and emit a tree diagram to stdout.', + epilog="Example: email-print-mime-structure < message.eml") + args:Namespace = parser.parse_args() msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) if isinstance(msg, Message): - printer:MimePrinter = MimePrinter() + printer:MimePrinter = MimePrinter(args) printer.test(msg, '└') else: logging.error('Input was not an e-mail message') diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod index ab1ec05..03a8e29 100644 --- a/email-print-mime-structure.1.pod +++ b/email-print-mime-structure.1.pod @@ -19,7 +19,14 @@ something like "cat -n". =head1 OPTIONS -None. +=over 4 + +=item B<--help>, B<-h> + +Show usage instructions. + +=back + =head1 EXAMPLE -- cgit v1.2.3 From abaf880e9bacd32f86c8210ab1489330320c2113 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:20 -0400 Subject: email-print-mime-structure: nbytes should show as a decimal integer No functional changes. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 38dc8d9..8fc8774 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -63,7 +63,7 @@ class MimePrinter(object): raise TypeError(f'expected payload to be either str or bytes, got {type(payload)}') nbytes = len(payload) - print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes} bytes') + print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') def test(self, z:Message, prefix:str='') -> None: if (z.is_multipart()): -- cgit v1.2.3 From 8dd6783b9bf37d0e12b343da6105cd92a5ebef1d Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:21 -0400 Subject: email-print-mime-structure: Pass parent and nth child info during walk No functional change. This is preparatory work to be able to consider the structure of each part and determine whether we should consider trying to decrypt it. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 8fc8774..98b35fe 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -42,7 +42,7 @@ class MimePrinter(object): def __init__(self, args:Namespace): self.args = args - def print_part(self, z:Message, prefix:str) -> None: + def print_part(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: ofname:Optional[str] = z.get_filename() fname:str = '' if ofname is None else f' [{ofname}]' ocharset:Union[Charset, str, None] = z.get_charset() @@ -65,9 +65,9 @@ class MimePrinter(object): print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') - def test(self, z:Message, prefix:str='') -> None: + def test(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: if (z.is_multipart()): - self.print_part(z, prefix+'┬╴') + self.print_part(z, prefix+'┬╴', parent, num) if prefix.endswith('└'): prefix = prefix.rpartition('└')[0] + ' ' if prefix.endswith('├'): @@ -77,12 +77,12 @@ class MimePrinter(object): raise TypeError(f'parts was {type(parts)}, expected List[Message]') i = 0 while (i < len(parts)-1): - self.test(parts[i], prefix + '├') + self.test(parts[i], prefix + '├', z, i+1) i += 1 - self.test(parts[i], prefix + '└') + self.test(parts[i], prefix + '└', z, i+1) # FIXME: show epilogue? else: - self.print_part(z, prefix+'─╴') + self.print_part(z, prefix+'─╴', parent, num) def main() -> None: parser:ArgumentParser = ArgumentParser(description='Read RFC2822 MIME message from stdin and emit a tree diagram to stdout.', @@ -92,7 +92,7 @@ def main() -> None: if isinstance(msg, Message): printer:MimePrinter = MimePrinter(args) - printer.test(msg, '└') + printer.test(msg, '└', None, 0) else: logging.error('Input was not an e-mail message') -- cgit v1.2.3 From d6dc4aaf0d465c3c267b9c8a663011b0b01fadcd Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:22 -0400 Subject: email-print-mime-structure: add another FIXME about bytecounting Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 1 + 1 file changed, 1 insertion(+) diff --git a/email-print-mime-structure b/email-print-mime-structure index 98b35fe..c1476d2 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -61,6 +61,7 @@ class MimePrinter(object): payload:Union[List[Message], str, bytes, None] = z.get_payload() if not isinstance(payload, (str,bytes)): raise TypeError(f'expected payload to be either str or bytes, got {type(payload)}') + # FIXME: it looks like we are counting chars here, not bytes: nbytes = len(payload) print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') -- cgit v1.2.3 From a858e19c0eb6c2a12d832b3dd256ef64c72f0fc1 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:23 -0400 Subject: email-print-mime-structure: renamed MimePrinter.test() to print_tree() No functional changes. This is just a more readable function name. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index c1476d2..33579a7 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -66,7 +66,7 @@ class MimePrinter(object): print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') - def test(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: + def print_tree(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: if (z.is_multipart()): self.print_part(z, prefix+'┬╴', parent, num) if prefix.endswith('└'): @@ -78,9 +78,9 @@ class MimePrinter(object): raise TypeError(f'parts was {type(parts)}, expected List[Message]') i = 0 while (i < len(parts)-1): - self.test(parts[i], prefix + '├', z, i+1) + self.print_tree(parts[i], prefix + '├', z, i+1) i += 1 - self.test(parts[i], prefix + '└', z, i+1) + self.print_tree(parts[i], prefix + '└', z, i+1) # FIXME: show epilogue? else: self.print_part(z, prefix+'─╴', parent, num) @@ -93,7 +93,7 @@ def main() -> None: if isinstance(msg, Message): printer:MimePrinter = MimePrinter(args) - printer.test(msg, '└', None, 0) + printer.print_tree(msg, '└', None, 0) else: logging.error('Input was not an e-mail message') -- cgit v1.2.3 From 67a847605769d5e255168a65d780594383569b75 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:28:24 -0400 Subject: email-print-mime-structure: add decryption capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add simple decryption capability for email-print-mime-structure, so that it can do stuff like this: $ email-print-mime-structure --pgpkey alice@openpgp.example.sec.asc < msg.eml └┬╴multipart/encrypted 2190 bytes ├─╴application/pgp-encrypted 11 bytes └─╴application/octet-stream 1613 bytes ↧ (decrypts to) └─╴text/plain 425 bytes $ At the moment, it only works with keys that can be found in the filesystem, and when the pgpy module is installed. Possible future work: - try using gpg to do the decryption from whatever gpg's system capabilities are I've added python3-pgpy to the list of Recommends, since it is not a hard dependency. Signed-off-by: Daniel Kahn Gillmor --- debian/control | 1 + email-print-mime-structure | 34 ++++++++++++++++++++++++++++++++++ email-print-mime-structure.1.pod | 8 ++++++++ 3 files changed, 43 insertions(+) diff --git a/debian/control b/debian/control index 6d3a54f..fc2bccc 100644 --- a/debian/control +++ b/debian/control @@ -39,6 +39,7 @@ Recommends: devscripts, git, notmuch, + python3-pgpy, Architecture: all Description: collection of scripts for manipulating e-mail on Debian This package provides a collection of scripts for manipulating e-mail diff --git a/email-print-mime-structure b/email-print-mime-structure index 33579a7..eb513b3 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -38,6 +38,11 @@ from typing import Optional, Union, List, Tuple, Any from email.charset import Charset from email.message import Message +try: + import pgpy #type: ignore +except ImportError: + pgpy = None + class MimePrinter(object): def __init__(self, args:Namespace): self.args = args @@ -66,6 +71,33 @@ class MimePrinter(object): print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') + if self.args.pgpkey and \ + (parent is not None) and \ + (parent.get_content_type().lower() == 'multipart/encrypted') and \ + (str(parent.get_param('protocol')).lower() == 'application/pgp-encrypted') and \ + (num == 2): + if pgpy is None: + logging.warning(f'Python module pgpy is not available, not decrypting (try "apt install python3-pgpy")') + else: + cryptopayload:Optional[Message] = None + keyname:str + for keyname in self.args.pgpkey: + try: + key:pgpy.PGPKey + key, _ = pgpy.PGPKey.from_file(keyname) + msg:pgpy.PGPMessage = pgpy.PGPMessage.from_blob(z.get_payload()) + msg = key.decrypt(msg) + cryptopayload = email.message_from_bytes(msg.message) + break + except: + pass + if cryptopayload is None: + logging.warning(f'Unable to decrypt') + else: + newprefix = prefix[:-3] + ' ' + print(f'{newprefix}↧ (decrypts to)') + self.print_tree(cryptopayload, newprefix + '└', z, 0) + def print_tree(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: if (z.is_multipart()): self.print_part(z, prefix+'┬╴', parent, num) @@ -88,6 +120,8 @@ class MimePrinter(object): def main() -> None: parser:ArgumentParser = ArgumentParser(description='Read RFC2822 MIME message from stdin and emit a tree diagram to stdout.', epilog="Example: email-print-mime-structure < message.eml") + parser.add_argument('--pgpkey', metavar='KEYFILE', action='append', + help='OpenPGP Transferable Secret Key for decrypting') args:Namespace = parser.parse_args() msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod index 03a8e29..209c725 100644 --- a/email-print-mime-structure.1.pod +++ b/email-print-mime-structure.1.pod @@ -21,6 +21,14 @@ something like "cat -n". =over 4 +=item B<--pgpkey=>I + +I should name an OpenPGP transferable secret key that is not +password-protected. If a PGP/MIME-encrypted message is found on +standard input, this key will be tried for decryption. May be used +multiple times if you want to try decrypting with more than one secret +key. + =item B<--help>, B<-h> Show usage instructions. -- cgit v1.2.3 From 172b3e7d459173b456dd18914b9b7fbe8c410805 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 2 Nov 2019 01:33:01 -0400 Subject: email-print-mime-structure.1.pod: update LIMITATIONS about OpenPGP decryption Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure.1.pod | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod index 209c725..b846d87 100644 --- a/email-print-mime-structure.1.pod +++ b/email-print-mime-structure.1.pod @@ -49,9 +49,10 @@ Show usage instructions. =head1 LIMITATIONS -B currently does not try to decrypt -encrypted e-mails, so it cannot display the MIME structure that is -inside the message's cryptographic envelope. +B only decrypts encrypted e-mails using +raw, non-password-protected OpenPGP secret keys (see B<--pgpkey>, +above). If it is unable to decrypt an encrypted part with the +supplied keys, it will warn on stderr. B's output is not stable, and is not intended to be interpreted by machines, so please do not depend on it -- cgit v1.2.3 From 7bb2f4ff1bdaf75ec33c5f885a47793f2508eedf Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 08:59:55 -0700 Subject: drop space before shell redirection operator For consistency with the manpage. Signed-off-by: Sean Whitton --- email-print-mime-structure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index eb513b3..644efb1 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -119,7 +119,7 @@ class MimePrinter(object): def main() -> None: parser:ArgumentParser = ArgumentParser(description='Read RFC2822 MIME message from stdin and emit a tree diagram to stdout.', - epilog="Example: email-print-mime-structure < message.eml") + epilog="Example: email-print-mime-structure Date: Sat, 2 Nov 2019 09:01:58 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/debian/changelog b/debian/changelog index 4de2082..149b4e1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,9 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium * email-print-mime-structure: make typesafe. Thanks to Daniel Kahn Gillmor for the patch. + * email-print-mime-structure: add capability to decrypt message parts + (Closes: #943959). + Thanks to Daniel Kahn Gillmor for the patch series. -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 -- cgit v1.2.3 From fdf112b63eb755fcaa5138567ed0428eea3957ff Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 09:17:44 -0700 Subject: update version in mailscripts.el Signed-off-by: Sean Whitton --- mailscripts.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailscripts.el b/mailscripts.el index f0002fc..3d3a584 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -1,7 +1,7 @@ ;;; mailscripts.el --- functions to access tools in the mailscripts package ;; Author: Sean Whitton -;; Version: 0.11 +;; Version: 0.12 ;; Package-Requires: (notmuch) ;; Copyright (C) 2018 Sean Whitton -- cgit v1.2.3 From 8d2dcb9d14a681c3ba86dc2f67756a5619f4d10d Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 09:52:46 -0700 Subject: mailscripts.el: add mailscripts-extract-patches-branch-prefix Signed-off-by: Sean Whitton --- debian/changelog | 1 + mailscripts.el | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/debian/changelog b/debian/changelog index 149b4e1..6f57add 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium * email-print-mime-structure: add capability to decrypt message parts (Closes: #943959). Thanks to Daniel Kahn Gillmor for the patch series. + * mailscripts.el: add mailscripts-extract-patches-branch-prefix defcustom. -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 diff --git a/mailscripts.el b/mailscripts.el index 3d3a584..a2ec230 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -4,7 +4,7 @@ ;; Version: 0.12 ;; Package-Requires: (notmuch) -;; Copyright (C) 2018 Sean Whitton +;; Copyright (C) 2018, 2019 Sean Whitton ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -23,6 +23,16 @@ (require 'notmuch) +(defgroup mailscripts nil + "Customisation of functions in the mailscripts package.") + +(defcustom mailscripts-extract-patches-branch-prefix nil + "Prefix for git branches created by functions which extract patch series. + +E.g. `mailed/'." + :type 'string + :group 'mailscripts) + ;;;###autoload (defun notmuch-slurp-debbug (bug &optional no-open) "Slurp Debian bug with bug number BUG and open the thread in notmuch. @@ -54,14 +64,20 @@ threads to the notmuch-extract-patch(1) command." (interactive "Dgit repo: \nsnew branch name: ") (let ((thread-id notmuch-show-thread-id) (default-directory (expand-file-name repo))) - (call-process-shell-command - (format "git checkout -b %s" - (shell-quote-argument branch))) + (mailscripts--check-out-branch branch) (shell-command (format "notmuch-extract-patch %s | git am" (shell-quote-argument thread-id)) "*notmuch-apply-thread-series*"))) +(defun mailscripts--check-out-branch (branch) + (call-process-shell-command + (format "git checkout -b %s" + (shell-quote-argument + (if mailscripts-extract-patches-branch-prefix + (concat mailscripts-extract-patches-branch-prefix branch) + branch))))) + (provide 'mailscripts) ;;; mailscripts.el ends here -- cgit v1.2.3 From cb53ee3309fb20492bdb2d43c4596ed507140701 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 10:06:39 -0700 Subject: mailscripts.el: add notmuch-extract-thread-patches-projectile Signed-off-by: Sean Whitton --- debian/changelog | 1 + mailscripts.el | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 6f57add..52c4282 100644 --- a/debian/changelog +++ b/debian/changelog @@ -6,6 +6,7 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium (Closes: #943959). Thanks to Daniel Kahn Gillmor for the patch series. * mailscripts.el: add mailscripts-extract-patches-branch-prefix defcustom. + * mailscripts.el: add notmuch-extract-thread-patches-projectile command. -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 diff --git a/mailscripts.el b/mailscripts.el index a2ec230..bcdc0ad 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -2,7 +2,7 @@ ;; Author: Sean Whitton ;; Version: 0.12 -;; Package-Requires: (notmuch) +;; Package-Requires: (notmuch projectile) ;; Copyright (C) 2018, 2019 Sean Whitton @@ -22,6 +22,7 @@ ;;; Code: (require 'notmuch) +(require 'projectile) (defgroup mailscripts nil "Customisation of functions in the mailscripts package.") @@ -70,6 +71,12 @@ threads to the notmuch-extract-patch(1) command." (shell-quote-argument thread-id)) "*notmuch-apply-thread-series*"))) +;;;###autoload +(defun notmuch-extract-thread-patches-projectile () + "Like `notmuch-extract-thread-patches', but use projectile to choose the repo." + (interactive) + (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) + (defun mailscripts--check-out-branch (branch) (call-process-shell-command (format "git checkout -b %s" @@ -78,6 +85,12 @@ threads to the notmuch-extract-patch(1) command." (concat mailscripts-extract-patches-branch-prefix branch) branch))))) +(defun mailscripts--projectile-repo-and-branch (f) + (let ((repo (projectile-completing-read + "Select projectile project: " projectile-known-projects)) + (branch (completing-read "Branch name: " nil))) + (funcall f repo branch))) + (provide 'mailscripts) ;;; mailscripts.el ends here -- cgit v1.2.3 From 80bf3c2b53d7514b888cec7ca6fdb2f4e579269d Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 12:06:40 -0700 Subject: mailscripts.el: add notmuch-extract-message-patches{,-projectile} Signed-off-by: Sean Whitton --- debian/changelog | 1 + mailscripts.el | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/debian/changelog b/debian/changelog index 52c4282..06e92e6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,6 +7,7 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium Thanks to Daniel Kahn Gillmor for the patch series. * mailscripts.el: add mailscripts-extract-patches-branch-prefix defcustom. * mailscripts.el: add notmuch-extract-thread-patches-projectile command. + * mailscripts.el: add notmuch-extract-message-patches{,-projectile} commands. -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 diff --git a/mailscripts.el b/mailscripts.el index bcdc0ad..09d213d 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -77,6 +77,35 @@ threads to the notmuch-extract-patch(1) command." (interactive) (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) +;;;###autoload +(defun notmuch-extract-message-patches (repo branch) + "Extract patches attached to current message to branch BRANCH in repo REPO. + +The target branch may or may not already exist. + +Patches are applied using git-am(1), so we only consider +attachments with filenames which look like they were generated by +git-format-patch(1)." + (interactive "Dgit repo: \nsnew branch name: ") + (with-current-notmuch-show-message + (let ((default-directory (expand-file-name repo)) + (mm-handle (mm-dissect-buffer))) + (mailscripts--check-out-branch branch) + (notmuch-foreach-mime-part + (lambda (p) + (let* ((disposition (mm-handle-disposition p)) + (filename (cdr (assq 'filename disposition)))) + (and filename + (string-match "^[0-9]+-.+\.\\(patch\\|diff\\|txt\\)$" filename) + (mm-pipe-part p "git am")))) + mm-handle)))) + +;;;###autoload +(defun notmuch-extract-message-patches-projectile () + "Like `notmuch-extract-message-patches', but use projectile to choose the repo." + (interactive) + (mailscripts--projectile-repo-and-branch 'notmuch-extract-message-patches)) + (defun mailscripts--check-out-branch (branch) (call-process-shell-command (format "git checkout -b %s" -- cgit v1.2.3 From 294e321dde7219b80d4fd6f831dff8546eab7ba3 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 12:16:37 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index 06e92e6..dc13036 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,7 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium * mailscripts.el: add mailscripts-extract-patches-branch-prefix defcustom. * mailscripts.el: add notmuch-extract-thread-patches-projectile command. * mailscripts.el: add notmuch-extract-message-patches{,-projectile} commands. + * elpa-mailscripts now depends on elpa-projectile. -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 -- cgit v1.2.3 From 3d33e7c0483bb9ab8de3ffd6a0372c8d2a0bffa0 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 12:33:25 -0700 Subject: make it possible to suppress prefixing to the branch name Signed-off-by: Sean Whitton --- mailscripts.el | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/mailscripts.el b/mailscripts.el index 09d213d..d4d7e95 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -54,43 +54,49 @@ If NO-OPEN, don't open the thread." (notmuch-refresh-this-buffer))) ;;;###autoload -(defun notmuch-extract-thread-patches (repo branch) +(defun notmuch-extract-thread-patches (repo branch &optional no-prefix) "Extract patch series in current thread to branch BRANCH in repo REPO. +A prefix arg suppresses the effects of +`mailscripts-extract-patches-branch-prefix'. + The target branch may or may not already exist. See notmuch-extract-patch(1) manpage for limitations: in particular, this Emacs Lisp function supports passing only entire threads to the notmuch-extract-patch(1) command." - (interactive "Dgit repo: \nsnew branch name: ") + (interactive "Dgit repo: \nsnew branch name: \nP") (let ((thread-id notmuch-show-thread-id) (default-directory (expand-file-name repo))) - (mailscripts--check-out-branch branch) + (mailscripts--check-out-branch branch no-prefix) (shell-command (format "notmuch-extract-patch %s | git am" (shell-quote-argument thread-id)) "*notmuch-apply-thread-series*"))) ;;;###autoload -(defun notmuch-extract-thread-patches-projectile () +(defun notmuch-extract-thread-patches-projectile (&optional no-prefix) "Like `notmuch-extract-thread-patches', but use projectile to choose the repo." - (interactive) + (interactive "P") (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) ;;;###autoload -(defun notmuch-extract-message-patches (repo branch) +(defun notmuch-extract-message-patches (repo branch &optional no-prefix) "Extract patches attached to current message to branch BRANCH in repo REPO. +A prefix arg suppresses the effects of +`mailscripts-extract-patches-branch-prefix'. + The target branch may or may not already exist. Patches are applied using git-am(1), so we only consider attachments with filenames which look like they were generated by git-format-patch(1)." - (interactive "Dgit repo: \nsnew branch name: ") + (interactive "Dgit repo: \nsnew branch name: \nP") (with-current-notmuch-show-message (let ((default-directory (expand-file-name repo)) (mm-handle (mm-dissect-buffer))) - (mailscripts--check-out-branch branch) + (mailscripts--check-out-branch branch no-prefix) (notmuch-foreach-mime-part (lambda (p) (let* ((disposition (mm-handle-disposition p)) @@ -101,16 +107,16 @@ git-format-patch(1)." mm-handle)))) ;;;###autoload -(defun notmuch-extract-message-patches-projectile () +(defun notmuch-extract-message-patches-projectile (&optional no-prefix) "Like `notmuch-extract-message-patches', but use projectile to choose the repo." - (interactive) + (interactive "P") (mailscripts--projectile-repo-and-branch 'notmuch-extract-message-patches)) -(defun mailscripts--check-out-branch (branch) +(defun mailscripts--check-out-branch (branch no-prefix) (call-process-shell-command (format "git checkout -b %s" (shell-quote-argument - (if mailscripts-extract-patches-branch-prefix + (if (and (not no-prefix) mailscripts-extract-patches-branch-prefix) (concat mailscripts-extract-patches-branch-prefix branch) branch))))) -- cgit v1.2.3 From cccf4e3f80da4582c81b3a014d5726922a0a0c85 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 2 Nov 2019 13:13:37 -0700 Subject: add possible 'vN-' prefix to git-format-patch(1) filename regexp Signed-off-by: Sean Whitton --- mailscripts.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mailscripts.el b/mailscripts.el index d4d7e95..d8f60a6 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -102,7 +102,8 @@ git-format-patch(1)." (let* ((disposition (mm-handle-disposition p)) (filename (cdr (assq 'filename disposition)))) (and filename - (string-match "^[0-9]+-.+\.\\(patch\\|diff\\|txt\\)$" filename) + (string-match + "^\\(v[0-9]+-\\)?[0-9]+-.+\.\\(patch\\|diff\\|txt\\)$" filename) (mm-pipe-part p "git am")))) mm-handle)))) -- cgit v1.2.3 From 34f83d0d1ce17e6ef05dfe16f9a70c937cf2df56 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 4 Nov 2019 15:34:15 -0700 Subject: thread no-prefix through the -projectile functions Signed-off-by: Sean Whitton --- mailscripts.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mailscripts.el b/mailscripts.el index d8f60a6..92cfe31 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -78,7 +78,8 @@ threads to the notmuch-extract-patch(1) command." (defun notmuch-extract-thread-patches-projectile (&optional no-prefix) "Like `notmuch-extract-thread-patches', but use projectile to choose the repo." (interactive "P") - (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) + (mailscripts--projectile-repo-and-branch + 'notmuch-extract-thread-patches no-prefix)) ;;;###autoload (defun notmuch-extract-message-patches (repo branch &optional no-prefix) @@ -111,7 +112,8 @@ git-format-patch(1)." (defun notmuch-extract-message-patches-projectile (&optional no-prefix) "Like `notmuch-extract-message-patches', but use projectile to choose the repo." (interactive "P") - (mailscripts--projectile-repo-and-branch 'notmuch-extract-message-patches)) + (mailscripts--projectile-repo-and-branch + 'notmuch-extract-message-patches no-prefix)) (defun mailscripts--check-out-branch (branch no-prefix) (call-process-shell-command @@ -121,11 +123,11 @@ git-format-patch(1)." (concat mailscripts-extract-patches-branch-prefix branch) branch))))) -(defun mailscripts--projectile-repo-and-branch (f) +(defun mailscripts--projectile-repo-and-branch (f &optional no-prefix) (let ((repo (projectile-completing-read "Select projectile project: " projectile-known-projects)) (branch (completing-read "Branch name: " nil))) - (funcall f repo branch))) + (funcall f repo branch no-prefix))) (provide 'mailscripts) -- cgit v1.2.3 From 23e42778e50eb030986ac6c5e98764f7775e359c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Nov 2019 20:31:50 -0700 Subject: tweak sample prefix Signed-off-by: Sean Whitton --- mailscripts.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailscripts.el b/mailscripts.el index 92cfe31..6bc64bc 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -30,7 +30,7 @@ (defcustom mailscripts-extract-patches-branch-prefix nil "Prefix for git branches created by functions which extract patch series. -E.g. `mailed/'." +E.g. `email/'." :type 'string :group 'mailscripts) -- cgit v1.2.3 From 736064843a60ac1814da71550ae04c9673ed1e89 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Nov 2019 20:34:53 -0700 Subject: mailscripts.el: if user does not enter a branch name, use HEAD Signed-off-by: Sean Whitton --- debian/changelog | 1 + mailscripts.el | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/debian/changelog b/debian/changelog index dc13036..919eb1a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,6 +7,7 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium Thanks to Daniel Kahn Gillmor for the patch series. * mailscripts.el: add mailscripts-extract-patches-branch-prefix defcustom. * mailscripts.el: add notmuch-extract-thread-patches-projectile command. + * mailscripts.el: if user does not enter a branch name, use current HEAD. * mailscripts.el: add notmuch-extract-message-patches{,-projectile} commands. * elpa-mailscripts now depends on elpa-projectile. diff --git a/mailscripts.el b/mailscripts.el index 6bc64bc..db1cf94 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -65,7 +65,8 @@ The target branch may or may not already exist. See notmuch-extract-patch(1) manpage for limitations: in particular, this Emacs Lisp function supports passing only entire threads to the notmuch-extract-patch(1) command." - (interactive "Dgit repo: \nsnew branch name: \nP") + (interactive + "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): \nP") (let ((thread-id notmuch-show-thread-id) (default-directory (expand-file-name repo))) (mailscripts--check-out-branch branch no-prefix) @@ -93,7 +94,8 @@ The target branch may or may not already exist. Patches are applied using git-am(1), so we only consider attachments with filenames which look like they were generated by git-format-patch(1)." - (interactive "Dgit repo: \nsnew branch name: \nP") + (interactive + "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): \nP") (with-current-notmuch-show-message (let ((default-directory (expand-file-name repo)) (mm-handle (mm-dissect-buffer))) @@ -116,17 +118,20 @@ git-format-patch(1)." 'notmuch-extract-message-patches no-prefix)) (defun mailscripts--check-out-branch (branch no-prefix) - (call-process-shell-command - (format "git checkout -b %s" - (shell-quote-argument - (if (and (not no-prefix) mailscripts-extract-patches-branch-prefix) - (concat mailscripts-extract-patches-branch-prefix branch) - branch))))) + (unless (string= branch "") + (call-process-shell-command + (format "git checkout -b %s" + (shell-quote-argument + (if (and (not no-prefix) mailscripts-extract-patches-branch-prefix) + (concat mailscripts-extract-patches-branch-prefix branch) + branch)))))) (defun mailscripts--projectile-repo-and-branch (f &optional no-prefix) (let ((repo (projectile-completing-read "Select projectile project: " projectile-known-projects)) - (branch (completing-read "Branch name: " nil))) + (branch (completing-read + "Branch name (or leave blank to apply to current HEAD): " + nil))) (funcall f repo branch no-prefix))) (provide 'mailscripts) -- cgit v1.2.3 From 1f2438926be9b6d5365362dc95441956aa453331 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Nov 2019 20:41:04 -0700 Subject: Revert "thread no-prefix through the -projectile functions" This reverts commit 34f83d0d1ce17e6ef05dfe16f9a70c937cf2df56. Signed-off-by: Sean Whitton --- mailscripts.el | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mailscripts.el b/mailscripts.el index db1cf94..43d06d0 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -79,8 +79,7 @@ threads to the notmuch-extract-patch(1) command." (defun notmuch-extract-thread-patches-projectile (&optional no-prefix) "Like `notmuch-extract-thread-patches', but use projectile to choose the repo." (interactive "P") - (mailscripts--projectile-repo-and-branch - 'notmuch-extract-thread-patches no-prefix)) + (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) ;;;###autoload (defun notmuch-extract-message-patches (repo branch &optional no-prefix) @@ -114,8 +113,7 @@ git-format-patch(1)." (defun notmuch-extract-message-patches-projectile (&optional no-prefix) "Like `notmuch-extract-message-patches', but use projectile to choose the repo." (interactive "P") - (mailscripts--projectile-repo-and-branch - 'notmuch-extract-message-patches no-prefix)) + (mailscripts--projectile-repo-and-branch 'notmuch-extract-message-patches)) (defun mailscripts--check-out-branch (branch no-prefix) (unless (string= branch "") @@ -126,13 +124,13 @@ git-format-patch(1)." (concat mailscripts-extract-patches-branch-prefix branch) branch)))))) -(defun mailscripts--projectile-repo-and-branch (f &optional no-prefix) +(defun mailscripts--projectile-repo-and-branch (f) (let ((repo (projectile-completing-read "Select projectile project: " projectile-known-projects)) (branch (completing-read "Branch name (or leave blank to apply to current HEAD): " nil))) - (funcall f repo branch no-prefix))) + (funcall f repo branch))) (provide 'mailscripts) -- cgit v1.2.3 From 9c2a3cafdd25d19d94e53942cf170afd6af635d7 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Nov 2019 20:42:28 -0700 Subject: Revert "make it possible to suppress prefixing to the branch name" This reverts commit 3d33e7c0483bb9ab8de3ffd6a0372c8d2a0bffa0. Commit 7360648, which allows the user to suppress branch checkout, is a better way to let the user override their configured mailscripts-extract-patches-branch-prefix. Signed-off-by: Sean Whitton --- mailscripts.el | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/mailscripts.el b/mailscripts.el index 43d06d0..1e8cf93 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -54,51 +54,45 @@ If NO-OPEN, don't open the thread." (notmuch-refresh-this-buffer))) ;;;###autoload -(defun notmuch-extract-thread-patches (repo branch &optional no-prefix) +(defun notmuch-extract-thread-patches (repo branch) "Extract patch series in current thread to branch BRANCH in repo REPO. -A prefix arg suppresses the effects of -`mailscripts-extract-patches-branch-prefix'. - The target branch may or may not already exist. See notmuch-extract-patch(1) manpage for limitations: in particular, this Emacs Lisp function supports passing only entire threads to the notmuch-extract-patch(1) command." (interactive - "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): \nP") + "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): ") (let ((thread-id notmuch-show-thread-id) (default-directory (expand-file-name repo))) - (mailscripts--check-out-branch branch no-prefix) + (mailscripts--check-out-branch branch) (shell-command (format "notmuch-extract-patch %s | git am" (shell-quote-argument thread-id)) "*notmuch-apply-thread-series*"))) ;;;###autoload -(defun notmuch-extract-thread-patches-projectile (&optional no-prefix) +(defun notmuch-extract-thread-patches-projectile () "Like `notmuch-extract-thread-patches', but use projectile to choose the repo." - (interactive "P") + (interactive) (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) ;;;###autoload -(defun notmuch-extract-message-patches (repo branch &optional no-prefix) +(defun notmuch-extract-message-patches (repo branch) "Extract patches attached to current message to branch BRANCH in repo REPO. -A prefix arg suppresses the effects of -`mailscripts-extract-patches-branch-prefix'. - The target branch may or may not already exist. Patches are applied using git-am(1), so we only consider attachments with filenames which look like they were generated by git-format-patch(1)." (interactive - "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): \nP") + "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): ") (with-current-notmuch-show-message (let ((default-directory (expand-file-name repo)) (mm-handle (mm-dissect-buffer))) - (mailscripts--check-out-branch branch no-prefix) + (mailscripts--check-out-branch branch) (notmuch-foreach-mime-part (lambda (p) (let* ((disposition (mm-handle-disposition p)) @@ -110,17 +104,17 @@ git-format-patch(1)." mm-handle)))) ;;;###autoload -(defun notmuch-extract-message-patches-projectile (&optional no-prefix) +(defun notmuch-extract-message-patches-projectile () "Like `notmuch-extract-message-patches', but use projectile to choose the repo." - (interactive "P") + (interactive) (mailscripts--projectile-repo-and-branch 'notmuch-extract-message-patches)) -(defun mailscripts--check-out-branch (branch no-prefix) +(defun mailscripts--check-out-branch (branch) (unless (string= branch "") (call-process-shell-command (format "git checkout -b %s" (shell-quote-argument - (if (and (not no-prefix) mailscripts-extract-patches-branch-prefix) + (if mailscripts-extract-patches-branch-prefix (concat mailscripts-extract-patches-branch-prefix branch) branch)))))) -- cgit v1.2.3 From b29caa46f35789e152fb0182441c33fac3382edb Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Nov 2019 20:53:57 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/debian/changelog b/debian/changelog index 919eb1a..f905ff5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,10 +5,13 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium * email-print-mime-structure: add capability to decrypt message parts (Closes: #943959). Thanks to Daniel Kahn Gillmor for the patch series. - * mailscripts.el: add mailscripts-extract-patches-branch-prefix defcustom. - * mailscripts.el: add notmuch-extract-thread-patches-projectile command. - * mailscripts.el: if user does not enter a branch name, use current HEAD. - * mailscripts.el: add notmuch-extract-message-patches{,-projectile} commands. + + * mailscripts.el: + - new defcustom: mailscripts-extract-patches-branch-prefix + - new commands: + + notmuch-extract-thread-patches-projectile + + notmuch-extract-message-patches{,-projectile} + - if user does not enter a branch name, use current HEAD. * elpa-mailscripts now depends on elpa-projectile. -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 -- cgit v1.2.3 From fbda073c3d28496a7882c9a261718de65470f40c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Nov 2019 20:55:01 -0700 Subject: release mailscript 0.12 (0.12-1 to Debian unstable) Signed-off-by: Sean Whitton --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index f905ff5..d5aa2a8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -mailscripts (0.12-1) UNRELEASED; urgency=medium +mailscripts (0.12-1) unstable; urgency=medium * email-print-mime-structure: make typesafe. Thanks to Daniel Kahn Gillmor for the patch. @@ -14,7 +14,7 @@ mailscripts (0.12-1) UNRELEASED; urgency=medium - if user does not enter a branch name, use current HEAD. * elpa-mailscripts now depends on elpa-projectile. - -- Sean Whitton Fri, 01 Nov 2019 20:38:07 -0700 + -- Sean Whitton Wed, 06 Nov 2019 20:54:56 -0700 mailscripts (0.11-1) unstable; urgency=medium -- cgit v1.2.3 From 519cf0fa2348e5022fc5e383e323364241d7b42f Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Nov 2019 11:05:45 -0700 Subject: notmuch-extract-patch: add -v/--reroll-count option Signed-off-by: Sean Whitton --- notmuch-extract-patch.1.pod | 31 +++++++++++++++++++---------- notmuch-extract-patch/notmuch-extract-patch | 24 ++++++++++++++++++++-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/notmuch-extract-patch.1.pod b/notmuch-extract-patch.1.pod index 21095bc..a18cc22 100644 --- a/notmuch-extract-patch.1.pod +++ b/notmuch-extract-patch.1.pod @@ -4,7 +4,7 @@ notmuch-extract-patch - extract a git patch series from notmuch =head1 SYNOPSIS -B I +B [B<-v>|B<--reroll-count=>I] I =head1 DESCRIPTION @@ -15,7 +15,23 @@ replies/reviews. =head1 OPTIONS -None. +=over 4 + +=item B<-v>|B<--reroll-count=>I + +Try to extract the Ith version of a patch series, where these +patches are identified by subject prefixes like "[PATCH vI 1/3]". + +If this option is not specified, default to extracting the first +version of the patch series. + +Note that this option should not usually be needed, because best +practices when sharing patches with git-send-email(1) include starting +a new thread when posting a revised series. The I<--in-reply-to> +option to git-format-patch(1) is used mainly for posting a patch +series in reply to a bug report. + +=back =head1 EXAMPLE @@ -28,17 +44,12 @@ None. =head1 LIMITATIONS -B assumes one patch series per query. So if -there is more than one patch series in a thread, you will need to +B can select patches to extract based on the +reroll count, but otherwise assumes that there is only one patch +series in a thread. If this assumption is violated, you would need to construct a notmuch query that includes only the patches you want to extract, which somewhat defeats the purpose of this script. -This should not happen often because best practices when sharing -patches with git-send-email(1) include starting a new thread when -posting a revised series. The I<--in-reply-to> option to -B is used mainly for posting a patch series in -reply to a bug report. - =head1 SEE ALSO notmuch(1), git-send-email(1) diff --git a/notmuch-extract-patch/notmuch-extract-patch b/notmuch-extract-patch/notmuch-extract-patch index cfd4464..4cfda4c 100755 --- a/notmuch-extract-patch/notmuch-extract-patch +++ b/notmuch-extract-patch/notmuch-extract-patch @@ -22,6 +22,7 @@ import sys import tempfile import subprocess import re +import getopt def get_body(message): body = None @@ -48,8 +49,27 @@ def is_git_patch(msg): # return ("git-send-email" in msg['x-mailer'] and match) return match +def has_reroll_count(msg, v): + subject_prefix = get_subject_prefix(msg['subject']) + if subject_prefix is not None: + return "v"+str(v) in subject_prefix \ + or (v == 1 and not any(entry[0] == 'v' for entry in subject_prefix)) + +def get_subject_prefix(s): + match = re.search(r'''^\[(.*PATCH.*)\]''', s) + if match: + return match.group(1).split() + def main(): - query = sys.argv[1:] + try: + opts, query = getopt.getopt(sys.argv[1:], "v:", ["reroll-count="]) + except getopt.GetoptError as err: + sys.stderr.write(str(err)+"\n") + sys.exit(2) + reroll_count = 1 + for o, a in opts: + if o in ("-v", "--reroll-count"): + reroll_count = int(a) with tempfile.NamedTemporaryFile() as in_mb_file: out = subprocess.check_output(['notmuch', 'show', '--format=mbox']+query) in_mb_file.write(out) @@ -59,7 +79,7 @@ def main(): with tempfile.NamedTemporaryFile() as out_mb_file: out_mb = mailbox.mbox(out_mb_file.name) for m in in_mb: - if is_git_patch(m): + if is_git_patch(m) and has_reroll_count(m, reroll_count): sys.stderr.write(m['subject']+"\n") out_mb.add(m) out_mb.flush() -- cgit v1.2.3 From 4c55ffc5e899e63603c09b0c37898243913e565c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Nov 2019 15:01:43 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 6 ++++++ mailscripts.el | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index d5aa2a8..80a9232 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +mailscripts (0.13-1) UNRELEASED; urgency=medium + + * notmuch-extract-patch: add -v/--reroll-count option + + -- Sean Whitton Sat, 09 Nov 2019 15:01:23 -0700 + mailscripts (0.12-1) unstable; urgency=medium * email-print-mime-structure: make typesafe. diff --git a/mailscripts.el b/mailscripts.el index 1e8cf93..ee05cd6 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -1,7 +1,7 @@ ;;; mailscripts.el --- functions to access tools in the mailscripts package ;; Author: Sean Whitton -;; Version: 0.12 +;; Version: 0.13 ;; Package-Requires: (notmuch projectile) ;; Copyright (C) 2018, 2019 Sean Whitton -- cgit v1.2.3 From fc0576a446b5d9cfee859184498780cce41b112d Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Nov 2019 15:03:53 -0700 Subject: close bug Signed-off-by: Sean Whitton --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 80a9232..545e749 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,6 @@ mailscripts (0.13-1) UNRELEASED; urgency=medium - * notmuch-extract-patch: add -v/--reroll-count option + * notmuch-extract-patch: add -v/--reroll-count option (Closes: #944418). -- Sean Whitton Sat, 09 Nov 2019 15:01:23 -0700 -- cgit v1.2.3 From f06554efd870911a275b6e920afb133d0d61e68c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Nov 2019 15:20:44 -0700 Subject: mailscripts.el: arg to pass --reroll-count to notmuch-extract-patch Signed-off-by: Sean Whitton --- debian/changelog | 2 ++ mailscripts.el | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/debian/changelog b/debian/changelog index 545e749..6787b43 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,8 @@ mailscripts (0.13-1) UNRELEASED; urgency=medium * notmuch-extract-patch: add -v/--reroll-count option (Closes: #944418). + * mailscripts.el: prefix arg to pass -v/--reroll-count to + notmuch-extract-patch. -- Sean Whitton Sat, 09 Nov 2019 15:01:23 -0700 diff --git a/mailscripts.el b/mailscripts.el index ee05cd6..916aec8 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -54,21 +54,26 @@ If NO-OPEN, don't open the thread." (notmuch-refresh-this-buffer))) ;;;###autoload -(defun notmuch-extract-thread-patches (repo branch) +(defun notmuch-extract-thread-patches (repo branch &optional reroll-count) "Extract patch series in current thread to branch BRANCH in repo REPO. The target branch may or may not already exist. +With an optional prefix numeric argument REROLL-COUNT, try to +extract the nth revision of a series. See the --reroll-count +option detailed in notmuch-extract-patch(1). + See notmuch-extract-patch(1) manpage for limitations: in particular, this Emacs Lisp function supports passing only entire threads to the notmuch-extract-patch(1) command." (interactive - "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): ") + "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): \np") (let ((thread-id notmuch-show-thread-id) (default-directory (expand-file-name repo))) (mailscripts--check-out-branch branch) (shell-command - (format "notmuch-extract-patch %s | git am" + (format "notmuch-extract-patch -v%d %s | git am" + (if reroll-count reroll-count 1) (shell-quote-argument thread-id)) "*notmuch-apply-thread-series*"))) @@ -76,7 +81,8 @@ threads to the notmuch-extract-patch(1) command." (defun notmuch-extract-thread-patches-projectile () "Like `notmuch-extract-thread-patches', but use projectile to choose the repo." (interactive) - (mailscripts--projectile-repo-and-branch 'notmuch-extract-thread-patches)) + (mailscripts--projectile-repo-and-branch + 'notmuch-extract-thread-patches (prefix-numeric-value current-prefix-arg))) ;;;###autoload (defun notmuch-extract-message-patches (repo branch) @@ -118,13 +124,13 @@ git-format-patch(1)." (concat mailscripts-extract-patches-branch-prefix branch) branch)))))) -(defun mailscripts--projectile-repo-and-branch (f) +(defun mailscripts--projectile-repo-and-branch (f &rest args) (let ((repo (projectile-completing-read "Select projectile project: " projectile-known-projects)) (branch (completing-read "Branch name (or leave blank to apply to current HEAD): " nil))) - (funcall f repo branch))) + (apply f repo branch args))) (provide 'mailscripts) -- cgit v1.2.3 From 02ef341d2280d168ebe045277e7ea1064b7650dd Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 19:14:21 -0500 Subject: wrap-and-sort -ast Signed-off-by: Daniel Kahn Gillmor --- debian/mailscripts.install | 10 +++++----- debian/mailscripts.manpages | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/debian/mailscripts.install b/debian/mailscripts.install index 99216c1..2c060df 100644 --- a/debian/mailscripts.install +++ b/debian/mailscripts.install @@ -1,8 +1,8 @@ +email-extract-openpgp-certs /usr/bin +email-print-mime-structure /usr/bin +maildir-import-patch /usr/bin mbox2maildir /usr/bin mdmv /usr/bin -notmuch-slurp-debbug /usr/bin -maildir-import-patch /usr/bin -notmuch-import-patch /usr/bin notmuch-extract-patch/notmuch-extract-patch /usr/bin -email-extract-openpgp-certs /usr/bin -email-print-mime-structure /usr/bin +notmuch-import-patch /usr/bin +notmuch-slurp-debbug /usr/bin diff --git a/debian/mailscripts.manpages b/debian/mailscripts.manpages index 6d7cb30..1de088f 100644 --- a/debian/mailscripts.manpages +++ b/debian/mailscripts.manpages @@ -1,8 +1,8 @@ +email-extract-openpgp-certs.1 +email-print-mime-structure.1 +maildir-import-patch.1 mbox2maildir.1 mdmv.1 -notmuch-slurp-debbug.1 -maildir-import-patch.1 -notmuch-import-patch.1 notmuch-extract-patch.1 -email-extract-openpgp-certs.1 -email-print-mime-structure.1 +notmuch-import-patch.1 +notmuch-slurp-debbug.1 -- cgit v1.2.3 From 04ae100b5f48f56443e3a99e14579c26d9811f4f Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 22:23:03 -0500 Subject: Fix typo Signed-off-by: Daniel Kahn Gillmor --- email-extract-openpgp-certs.1.pod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/email-extract-openpgp-certs.1.pod b/email-extract-openpgp-certs.1.pod index 9983de0..d1d641a 100644 --- a/email-extract-openpgp-certs.1.pod +++ b/email-extract-openpgp-certs.1.pod @@ -38,7 +38,7 @@ message's cryptographic envelope. B does not attempt to validate the certificates it finds in any way. It does not ensure that they are valid OpenPGP certificates, or even that they are of a sane size. It -doeds not try to establish any relationship between the extracted +does not try to establish any relationship between the extracted certificates and the messages in which they are sent. For example, it does not check the Autocrypt addr= attribute against the message's From: header. -- cgit v1.2.3 From 3de0b3a9492da7609409ecb1b652aef70d848823 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 16:48:11 -0500 Subject: email-print-mime-structure: sanity check cryptographic payload We want to make sure we're decrypting the thing that we expect. This typecheck should keep us honest. Signed-off-by: Daniel Kahn Gillmor Acked-by: Sean Whitton --- email-print-mime-structure | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 644efb1..2cbf6ed 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -76,16 +76,20 @@ class MimePrinter(object): (parent.get_content_type().lower() == 'multipart/encrypted') and \ (str(parent.get_param('protocol')).lower() == 'application/pgp-encrypted') and \ (num == 2): + cryptopayload:Optional[Message] = None + ciphertext:Union[List[Message],str,bytes,None] = z.get_payload() + if not isinstance(ciphertext, str): + logging.warning('encrypted part was not a leaf mime part somehow') + return if pgpy is None: logging.warning(f'Python module pgpy is not available, not decrypting (try "apt install python3-pgpy")') else: - cryptopayload:Optional[Message] = None keyname:str for keyname in self.args.pgpkey: try: key:pgpy.PGPKey key, _ = pgpy.PGPKey.from_file(keyname) - msg:pgpy.PGPMessage = pgpy.PGPMessage.from_blob(z.get_payload()) + msg:pgpy.PGPMessage = pgpy.PGPMessage.from_blob(ciphertext) msg = key.decrypt(msg) cryptopayload = email.message_from_bytes(msg.message) break -- cgit v1.2.3 From b7a26158a2a87b51de86621107c01b24c2c8952d Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 16:48:12 -0500 Subject: email-print-mime-structure: Restructure pgpy decryption This has no functional changes, it's just a reorganization for easier readability. Thanks to Sean Whitton for the suggestion. Signed-off-by: Daniel Kahn Gillmor Acked-by: Sean Whitton --- email-print-mime-structure | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 2cbf6ed..4f46f58 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -81,26 +81,30 @@ class MimePrinter(object): if not isinstance(ciphertext, str): logging.warning('encrypted part was not a leaf mime part somehow') return - if pgpy is None: - logging.warning(f'Python module pgpy is not available, not decrypting (try "apt install python3-pgpy")') - else: - keyname:str - for keyname in self.args.pgpkey: - try: - key:pgpy.PGPKey - key, _ = pgpy.PGPKey.from_file(keyname) - msg:pgpy.PGPMessage = pgpy.PGPMessage.from_blob(ciphertext) - msg = key.decrypt(msg) - cryptopayload = email.message_from_bytes(msg.message) - break - except: - pass - if cryptopayload is None: - logging.warning(f'Unable to decrypt') - else: - newprefix = prefix[:-3] + ' ' - print(f'{newprefix}↧ (decrypts to)') - self.print_tree(cryptopayload, newprefix + '└', z, 0) + cryptopayload = self.pgpy_decrypt(self.args.pgpkey, ciphertext) + if cryptopayload is None: + logging.warning(f'Unable to decrypt') + return + newprefix = prefix[:-3] + ' ' + print(f'{newprefix}↧ (decrypts to)') + self.print_tree(cryptopayload, newprefix + '└', z, 0) + + def pgpy_decrypt(self, keys:List[str], ciphertext:str) -> Optional[Message]: + if pgpy is None: + logging.warning(f'Python module pgpy is not available, not decrypting (try "apt install python3-pgpy")') + return None + keyname:str + ret:Optional[Message] = None + for keyname in keys: + try: + key:pgpy.PGPKey + key, _ = pgpy.PGPKey.from_file(keyname) + msg:pgpy.PGPMessage = pgpy.PGPMessage.from_blob(ciphertext) + msg = key.decrypt(msg) + return email.message_from_bytes(msg.message) + except: + pass + return None def print_tree(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: if (z.is_multipart()): -- cgit v1.2.3 From bc35cd2bd19d4e29c46289c831170327f5c8e161 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 16:48:13 -0500 Subject: email-print-mime-structure: prepare for other decryption mechanisms No functional change here: this just prepares for adding other decryption capabilities. Signed-off-by: Daniel Kahn Gillmor Acked-by: Sean Whitton --- email-print-mime-structure | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index 4f46f58..c22d556 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -70,8 +70,9 @@ class MimePrinter(object): nbytes = len(payload) print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') + try_decrypt:bool = True if self.args.pgpkey else False - if self.args.pgpkey and \ + if try_decrypt and \ (parent is not None) and \ (parent.get_content_type().lower() == 'multipart/encrypted') and \ (str(parent.get_param('protocol')).lower() == 'application/pgp-encrypted') and \ @@ -81,7 +82,8 @@ class MimePrinter(object): if not isinstance(ciphertext, str): logging.warning('encrypted part was not a leaf mime part somehow') return - cryptopayload = self.pgpy_decrypt(self.args.pgpkey, ciphertext) + if self.args.pgpkey: + cryptopayload = self.pgpy_decrypt(self.args.pgpkey, ciphertext) if cryptopayload is None: logging.warning(f'Unable to decrypt') return -- cgit v1.2.3 From e910230a9fb8a5151bede6d043679ec50570290f Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 16:48:14 -0500 Subject: email-print-mime-structure: Add --use-gpg-agent for decryption In some cases, the user may want to try to use their own GnuPG secret keys to decrypt encrypted parts of the message. By default it is disabled so that we aren't accidentally triggering the use of user secret key material. Note that gpg(1) says: It is highly recommended to use [--batch] along with the options --status-fd and --with-colons for any unattended use of gpg. I am deliberately choosing to not use either --status-fd or --with-colons for email-print-mime-structure. I'm not using --with-colons because there is no output from GnuPG that we expect to be machine-readable -- we're just looking for the cleartext of whatever ciphertext is in the message part. I'm not using --status-fd because there is nothing actionable we can do with GnuPG status messages, and asking for them would require switching from subprocess.run to subprocess.Popen to take advantage of the pass_fds argument, which in turn would make the script only work in a POSIX environment (I believe, but have not tested, that the script can currently be used on Windows). Signed-off-by: Daniel Kahn Gillmor --- debian/control | 2 ++ email-print-mime-structure | 22 +++++++++++++++++++++- email-print-mime-structure.1.pod | 24 +++++++++++++++++++----- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/debian/control b/debian/control index fc2bccc..4c3b956 100644 --- a/debian/control +++ b/debian/control @@ -38,6 +38,8 @@ Depends: Recommends: devscripts, git, + gpg, + gpg-agent, notmuch, python3-pgpy, Architecture: all diff --git a/email-print-mime-structure b/email-print-mime-structure index c22d556..5497597 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -29,9 +29,11 @@ Example: If you want to number the parts, i suggest piping the output through something like "cat -n" ''' +import os import sys import email import logging +import subprocess from argparse import ArgumentParser, Namespace from typing import Optional, Union, List, Tuple, Any @@ -70,7 +72,7 @@ class MimePrinter(object): nbytes = len(payload) print(f'{prefix}{z.get_content_type()}{cset}{disposition}{fname} {nbytes:d} bytes') - try_decrypt:bool = True if self.args.pgpkey else False + try_decrypt:bool = self.args.pgpkey or self.args.use_gpg_agent if try_decrypt and \ (parent is not None) and \ @@ -84,6 +86,8 @@ class MimePrinter(object): return if self.args.pgpkey: cryptopayload = self.pgpy_decrypt(self.args.pgpkey, ciphertext) + if cryptopayload is None and self.args.use_gpg_agent: + cryptopayload = self.gpg_decrypt(ciphertext) if cryptopayload is None: logging.warning(f'Unable to decrypt') return @@ -108,6 +112,19 @@ class MimePrinter(object): pass return None + def gpg_decrypt(self, ciphertext:str) -> Optional[Message]: + inp:int + outp:int + inp, outp = os.pipe() + with open(outp, 'w') as outf: + outf.write(ciphertext) + out:subprocess.CompletedProcess[bytes] = subprocess.run(['gpg', '--batch', '--decrypt'], + stdin=inp, + capture_output=True) + if out.returncode == 0: + return email.message_from_bytes(out.stdout) + return None + def print_tree(self, z:Message, prefix:str, parent:Optional[Message], num:int) -> None: if (z.is_multipart()): self.print_part(z, prefix+'┬╴', parent, num) @@ -132,6 +149,9 @@ def main() -> None: epilog="Example: email-print-mime-structure are used ephemerally, and +do not interact with any local GnuPG keyring. + +=item B<--use-gpg-agent=>I|I + +If I, and B encounters a +PGP/MIME-encrypted part, it will try to decrypt the part using the +secret keys found in the local installation of GnuPG. (default: +I) + +If both B<--pgpkey=>I and B<--use-gpg-agent=true> are +supplied, I arguments will be tried before falling back to +GnuPG. + +If B has been asked to decrypt parts with +either B<--pgpkey=>I or with B<--use-gpg-agent=true>, and it +is unable to decrypt an encrypted part, it will emit a warning to +stderr. + =item B<--help>, B<-h> Show usage instructions. @@ -49,11 +68,6 @@ Show usage instructions. =head1 LIMITATIONS -B only decrypts encrypted e-mails using -raw, non-password-protected OpenPGP secret keys (see B<--pgpkey>, -above). If it is unable to decrypt an encrypted part with the -supplied keys, it will warn on stderr. - B's output is not stable, and is not intended to be interpreted by machines, so please do not depend on it in scripts! -- cgit v1.2.3 From 95bd865a3f220e6b91c9322b07e889ccae966f63 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 9 Nov 2019 16:48:15 -0500 Subject: email-print-mime-structure(1): add a reference for PGP/MIME RFC 3156 documents PGP/MIME structural assumptions Signed-off-by: Daniel Kahn Gillmor Acked-by: Sean Whitton --- email-print-mime-structure.1.pod | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod index 69b1cdc..e4634e6 100644 --- a/email-print-mime-structure.1.pod +++ b/email-print-mime-structure.1.pod @@ -82,7 +82,8 @@ environment. =head1 SEE ALSO -https://tools.ietf.org/html/rfc2045, https://tools.ietf.org/html/rfc2049 +https://tools.ietf.org/html/rfc2045, https://tools.ietf.org/html/rfc2049, +https://tools.ietf.org/html/rfc3156 =head1 AUTHOR -- cgit v1.2.3 From d1ac3aa761e03486915c98348206970db80a3352 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Nov 2019 17:07:39 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debian/changelog b/debian/changelog index 6787b43..85e3e47 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,8 @@ mailscripts (0.13-1) UNRELEASED; urgency=medium * notmuch-extract-patch: add -v/--reroll-count option (Closes: #944418). * mailscripts.el: prefix arg to pass -v/--reroll-count to notmuch-extract-patch. + * email-print-mime-structure: add --use-gpg-agent option (Closes: #944340). + Thanks to Daniel Kahn Gillmor for the patch series. -- Sean Whitton Sat, 09 Nov 2019 15:01:23 -0700 -- cgit v1.2.3 From 92eaaca02a0945e2ede9b54975ed218ece9632c3 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 10 Nov 2019 01:00:47 -0700 Subject: demote gpg, gpg-agent Recommends->Suggests See discussion in #944340. Signed-off-by: Sean Whitton --- debian/changelog | 1 + debian/control | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 85e3e47..af4030a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ mailscripts (0.13-1) UNRELEASED; urgency=medium notmuch-extract-patch. * email-print-mime-structure: add --use-gpg-agent option (Closes: #944340). Thanks to Daniel Kahn Gillmor for the patch series. + - Suggest gpg & gpg-agent. -- Sean Whitton Sat, 09 Nov 2019 15:01:23 -0700 diff --git a/debian/control b/debian/control index 4c3b956..f92f7a1 100644 --- a/debian/control +++ b/debian/control @@ -38,10 +38,11 @@ Depends: Recommends: devscripts, git, - gpg, - gpg-agent, notmuch, python3-pgpy, +Suggests: + gpg, + gpg-agent, Architecture: all Description: collection of scripts for manipulating e-mail on Debian This package provides a collection of scripts for manipulating e-mail -- cgit v1.2.3 From 677d6ed933a073a3bc3b2c461f49a97b0cbefebd Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 10 Nov 2019 01:12:18 -0700 Subject: release mailscript 0.13 (0.13-1 to Debian unstable) Signed-off-by: Sean Whitton --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index af4030a..ef96e0e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -mailscripts (0.13-1) UNRELEASED; urgency=medium +mailscripts (0.13-1) unstable; urgency=medium * notmuch-extract-patch: add -v/--reroll-count option (Closes: #944418). * mailscripts.el: prefix arg to pass -v/--reroll-count to @@ -7,7 +7,7 @@ mailscripts (0.13-1) UNRELEASED; urgency=medium Thanks to Daniel Kahn Gillmor for the patch series. - Suggest gpg & gpg-agent. - -- Sean Whitton Sat, 09 Nov 2019 15:01:23 -0700 + -- Sean Whitton Sun, 10 Nov 2019 01:12:04 -0700 mailscripts (0.12-1) unstable; urgency=medium -- cgit v1.2.3 From 818dba1efe67f7b01f6d601c6462a40567c9ed7f Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 10 Nov 2019 09:31:58 -0500 Subject: email-print-mime-structure: add tab completion This is modeled after the use of argcomplete in diffoscope, and it should be possible to use it for any other pythonic mailscript that uses argparse. Signed-off-by: Daniel Kahn Gillmor --- Makefile | 9 ++++++++- debian/control | 3 +++ debian/mailscripts.bash-completion | 1 + debian/rules | 2 +- email-print-mime-structure | 15 +++++++++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 debian/mailscripts.bash-completion diff --git a/Makefile b/Makefile index 352f6f0..0cd06b7 100644 --- a/Makefile +++ b/Makefile @@ -3,14 +3,21 @@ MANPAGES=mdmv.1 mbox2maildir.1 \ email-extract-openpgp-certs.1 \ email-print-mime-structure.1 \ notmuch-import-patch.1 +COMPLETIONS=completions/bash/email-print-mime-structure -all: $(MANPAGES) +all: $(MANPAGES) $(COMPLETIONS) clean: rm -f $(MANPAGES) + rm -rf completions %.1: %.1.pod pod2man --section=1 --date="Debian Project" --center="User Commands" \ --utf8 \ --name=$(subst .1,,$@) \ $^ $@ + +completions/bash/%: + mkdir -p completions/bash + register-python-argcomplete3 $(notdir $@) > $@.tmp + mv $@.tmp $@ diff --git a/debian/control b/debian/control index f92f7a1..782636f 100644 --- a/debian/control +++ b/debian/control @@ -4,9 +4,11 @@ Priority: optional Maintainer: Sean Whitton Standards-Version: 4.1.5 Build-Depends: + bash-completion, debhelper (>= 10), dh-elpa, perl, + python3-argcomplete, Vcs-Git: https://git.spwhitton.name/mailscripts Vcs-Browser: https://git.spwhitton.name/mailscripts @@ -39,6 +41,7 @@ Recommends: devscripts, git, notmuch, + python3-argcomplete, python3-pgpy, Suggests: gpg, diff --git a/debian/mailscripts.bash-completion b/debian/mailscripts.bash-completion new file mode 100644 index 0000000..435576f --- /dev/null +++ b/debian/mailscripts.bash-completion @@ -0,0 +1 @@ +completions/bash/email-print-mime-structure diff --git a/debian/rules b/debian/rules index e8e22ba..6d50bf4 100755 --- a/debian/rules +++ b/debian/rules @@ -1,4 +1,4 @@ #!/usr/bin/make -f %: - dh $@ --with elpa + dh $@ --with elpa --with bash-completion diff --git a/email-print-mime-structure b/email-print-mime-structure index 5497597..aac8194 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK # -*- coding: utf-8 -*- # Copyright (C) 2019 Daniel Kahn Gillmor @@ -45,6 +46,11 @@ try: except ImportError: pgpy = None +try: + import argcomplete #type: ignore +except ImportError: + argcomplete = None + class MimePrinter(object): def __init__(self, args:Namespace): self.args = args @@ -152,6 +158,15 @@ def main() -> None: parser.add_argument('--use-gpg-agent', metavar='true|false', type=bool, default=False, help='Ask local GnuPG installation for decryption') + + if argcomplete: + argcomplete.autocomplete(parser) + elif '_ARGCOMPLETE' in os.environ: + logging.error('Argument completion requested but the "argcomplete" ' + 'module is not installed. ' + 'Maybe you want to "apt install python3-argcomplete"') + sys.exit(1) + args:Namespace = parser.parse_args() msg:Union[Message, str, int, Any] = email.message_from_file(sys.stdin) -- cgit v1.2.3 From 5e0ca1b49f6ac2e649f4c4942616c782fe6a13a5 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 10 Nov 2019 09:00:40 -0700 Subject: normalise shell redirect Signed-off-by: Sean Whitton --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0cd06b7..cd8f592 100644 --- a/Makefile +++ b/Makefile @@ -19,5 +19,5 @@ clean: completions/bash/%: mkdir -p completions/bash - register-python-argcomplete3 $(notdir $@) > $@.tmp + register-python-argcomplete3 $(notdir $@) >$@.tmp mv $@.tmp $@ -- cgit v1.2.3 From 55603ae831d2a50c7b2fb37b7a8cce1690d11b5e Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 10 Nov 2019 09:02:29 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index ef96e0e..09441a6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +mailscripts (0.14-1) UNRELEASED; urgency=medium + + * email-print-mime-structure: add bash completion (Closes: #944434). + Thanks to Daniel Kahn Gillmor for the patch. + + -- Sean Whitton Sun, 10 Nov 2019 09:01:58 -0700 + mailscripts (0.13-1) unstable; urgency=medium * notmuch-extract-patch: add -v/--reroll-count option (Closes: #944418). -- cgit v1.2.3 From 8ef023c49c264bb77f2d1a64a0ba3c0630407de7 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sun, 10 Nov 2019 09:06:44 -0700 Subject: Declare compliance with Debian Policy 4.4.1 Thanks to Daniel Kahn Gillmor for taking the time to verify that no changes are required. Signed-off-by: Sean Whitton --- debian/changelog | 3 +++ debian/control | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 09441a6..caf14cc 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,9 @@ mailscripts (0.14-1) UNRELEASED; urgency=medium * email-print-mime-structure: add bash completion (Closes: #944434). Thanks to Daniel Kahn Gillmor for the patch. + * Declare compliance with Debian Policy 4.4.1. + Thanks to Daniel Kahn Gillmor for taking the time to verify that no + changes are required. -- Sean Whitton Sun, 10 Nov 2019 09:01:58 -0700 diff --git a/debian/control b/debian/control index 782636f..72b57c3 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: mailscripts Section: mail Priority: optional Maintainer: Sean Whitton -Standards-Version: 4.1.5 +Standards-Version: 4.4.1 Build-Depends: bash-completion, debhelper (>= 10), -- cgit v1.2.3 From b9a31898cfe7fd448754a98fdaa8b4145509150d Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 10 Nov 2019 11:47:36 -0500 Subject: email-print-mime-structure: change --use-gpg-agent to a simple flag Turns out that type=bool doesn't really do what we want it to do (see https://bugs.python.org/issue37564), and there is no built-in easy answer for argparse to accept a boolean value sensibly (e.g. type='bool', which might be able to handle "yes" and "no" and "1" and "0" and "on" and "off" as well as "true" and "false", etc) So rather than implement all of that here, we'll just have --use-gpg-agent as a simple flag. This is an API change, but the previous API has only been out for a few days, and the tool is documented for interactive use. Signed-off-by: Daniel Kahn Gillmor --- email-print-mime-structure | 4 ++-- email-print-mime-structure.1.pod | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/email-print-mime-structure b/email-print-mime-structure index aac8194..3f29fb9 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -155,9 +155,9 @@ def main() -> None: epilog="Example: email-print-mime-structure are used ephemerally, and do not interact with any local GnuPG keyring. -=item B<--use-gpg-agent=>I|I +=item B<--use-gpg-agent> -If I, and B encounters a -PGP/MIME-encrypted part, it will try to decrypt the part using the -secret keys found in the local installation of GnuPG. (default: -I) +If this flag is present, and B encounters +a PGP/MIME-encrypted part, it will try to decrypt the part using the +secret keys found in the local installation of GnuPG. -If both B<--pgpkey=>I and B<--use-gpg-agent=true> are +If both B<--pgpkey=>I and B<--use-gpg-agent> are supplied, I arguments will be tried before falling back to GnuPG. If B has been asked to decrypt parts with -either B<--pgpkey=>I or with B<--use-gpg-agent=true>, and it +either B<--pgpkey=>I or with B<--use-gpg-agent>, and it is unable to decrypt an encrypted part, it will emit a warning to stderr. -- cgit v1.2.3 From 2bb2b573ad9ceaee58986b04fd4688fec4129569 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 15 Nov 2019 18:00:44 -0700 Subject: email-print-mime-structure: add --no-use-gpg-agent This allows the user to avoid being affected by any future change in the default. Signed-off-by: Sean Whitton --- email-print-mime-structure | 2 ++ email-print-mime-structure.1.pod | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/email-print-mime-structure b/email-print-mime-structure index 3f29fb9..4f165b1 100755 --- a/email-print-mime-structure +++ b/email-print-mime-structure @@ -157,6 +157,8 @@ def main() -> None: help='OpenPGP Transferable Secret Key for decrypting') parser.add_argument('--use-gpg-agent', action='store_true', help='Ask local GnuPG installation for decryption') + parser.add_argument('--no-use-gpg-agent', action='store_false', + help='Don\'t ask local GnuPG installation for decryption') parser.set_defaults(use_gpg_agent=False) if argcomplete: diff --git a/email-print-mime-structure.1.pod b/email-print-mime-structure.1.pod index 7201f48..d8545ad 100644 --- a/email-print-mime-structure.1.pod +++ b/email-print-mime-structure.1.pod @@ -47,6 +47,11 @@ either B<--pgpkey=>I or with B<--use-gpg-agent>, and it is unable to decrypt an encrypted part, it will emit a warning to stderr. +=item B<--no-use-gpg-agent> + +Don't try to decrypt PGP/MIME-encrypted parts using secret keys found +in the local installation of GnuPG. This is the default. + =item B<--help>, B<-h> Show usage instructions. -- cgit v1.2.3 From bb2eb59ef72b92e598f149d3987f0969386223ab Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 15 Nov 2019 18:02:48 -0700 Subject: changelog Signed-off-by: Sean Whitton --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index caf14cc..2c200f5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,12 @@ mailscripts (0.14-1) UNRELEASED; urgency=medium * email-print-mime-structure: add bash completion (Closes: #944434). Thanks to Daniel Kahn Gillmor for the patch. + - Build-depend on bash-completion, python3-argcomplete. + - Recommend python3-argcomplete. + * email-print-mime-structure: replace --use-gpg-agent=true with + --use-gpg-agent, and add --no-use-gpg-agent (Closes: #944475). + This is due to limitations in Python's argparse library. + Thanks to Daniel Kahn Gillmor for the report and a patch. * Declare compliance with Debian Policy 4.4.1. Thanks to Daniel Kahn Gillmor for taking the time to verify that no changes are required. -- cgit v1.2.3 From 693117551a0e21359ac6dbadba443516c56b04df Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 15 Nov 2019 18:19:09 -0700 Subject: release mailscripts 0.14 (0.14-1 to Debian unstable) Signed-off-by: Sean Whitton --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 2c200f5..9f6a0d1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -mailscripts (0.14-1) UNRELEASED; urgency=medium +mailscripts (0.14-1) unstable; urgency=medium * email-print-mime-structure: add bash completion (Closes: #944434). Thanks to Daniel Kahn Gillmor for the patch. @@ -12,7 +12,7 @@ mailscripts (0.14-1) UNRELEASED; urgency=medium Thanks to Daniel Kahn Gillmor for taking the time to verify that no changes are required. - -- Sean Whitton Sun, 10 Nov 2019 09:01:58 -0700 + -- Sean Whitton Fri, 15 Nov 2019 18:19:04 -0700 mailscripts (0.13-1) unstable; urgency=medium -- cgit v1.2.3