blob: 2e73bc7ad8924145d4a0a60e1459622d3df6638e (
plain) (
blame)
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
|
#!/usr/bin/ruby
# Copyright 2019 Alyssa Ross
#
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
at_exit do
case $!
when nil, SystemExit
else
$stderr.puts "unpgpmime: #$!"
exit! 1
end
end
require "mail"
require "open3"
require "optparse"
OptionParser.new do |opts|
opts.banner = <<~BANNER
Usage: #$0 [OPTION]... [FILE]
Strip PGP/MIME encryption from FILE (standard input by default).
BANNER
opts.on "-h", "--help", "show this usage display" do
puts opts
exit
end
end.parse!
def decrypt(data)
result, status = Open3.capture2(*%w[gpg --no-batch --decrypt], stdin_data: data)
fail "gpg failed with exit code #{status.to_i}" unless status.success?
result
end
def validate_multipart_encrypted(message)
part_types = message.parts.map(&:content_type)
expected_part_types = %w[application/pgp-encrypted application/octet-stream]
unless part_types.difference(expected_part_types).empty?
fail "unexpected or missing parts of multipart/encrypted"
end
pgp_encrypted_type = "application/pgp-encrypted"
pgp_encrypted = message.parts.find { |p| p.content_type == pgp_encrypted_type }
pgp_content = Mail::Part.new(pgp_encrypted.body)
if pgp_content["Version"].value != "1"
fail "unknown application/pgp-encrypted version"
end
end
def strip_pgp(part)
return part unless part.content_type =~ %r{\Amultipart/encrypted(;|\z)}
return part unless part.content_type_parameters["protocol"] == "application/pgp-encrypted"
validate_multipart_encrypted(part)
new_part = Mail::Part.new
encrypted = part.parts.find { |p| p.content_type == "application/octet-stream" }
decrypted = Mail::Part.new(decrypt(encrypted.read))
encrypted_fields = decrypted.header_fields.group_by(&:name)
part.header_fields.each do |field|
next if encrypted_fields.include?(field.name)
new_part.headers(field.name => field.value)
end
decrypted.parts.each do |part|
new_part.add_part(strip_pgp(part))
end
new_part.body = decrypted.body.to_s if new_part.parts.empty?
new_part
end
fail "multiple messages are not supported" if ARGV.size > 1
message = Mail::Message.new($<.read)
print strip_pgp(message)
|