initial commit for edward-bot
[edward.git] / edward-bot
CommitLineData
0bec96d6
AE
1#! /usr/bin/env python3
2
3"""*********************************************************************
4* Edward is free software: you can redistribute it and/or modify *
5* it under the terms of the GNU Affero Public License as published by *
6* the Free Software Foundation, either version 3 of the License, or *
7* (at your option) any later version. *
8* *
9* Edward is distributed in the hope that it will be useful, *
10* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12* GNU Affero Public License for more details. *
13* *
14* You should have received a copy of the GNU Affero Public License *
15* along with Edward. If not, see <http://www.gnu.org/licenses/>. *
16* *
17* Copyright (C) 2014-2015 Andrew Engelbrecht (AGPLv3+) *
18* Copyright (C) 2014 Josh Drake (AGPLv3+) *
19* Copyright (C) 2014 Lisa Marie Maginnis (AGPLv3+) *
20* *
21* Special thanks to Josh Drake for writing the original edward bot! :) *
22* *
23************************************************************************
24
25Code used from:
26
27* http://agpl.fsf.org/emailselfdefense.fsf.org/edward/CURRENT/edward.tar.gz
28
29"""
30
31import sys
32import email.parser
33import gpgme
34import re
35import io
36import time
37
38def main ():
39
40 txt = sys.stdin.read()
41
42 msg = email.parser.Parser().parsestr(txt)
43
44 print("From: " + msg['From'])
45 print("Subject: " + msg['Subject'])
46 print()
47
48 msg_walk(msg)
49
50
51def msg_walk (msg):
52
53 for part in msg.walk():
54 if part.get_content_type() == 'multipart':
55 continue
56 else:
57 filename = part.get_filename()
58 conttype = part.get_content_type()
59 descrip_p = part.get_params(header='content-description')
60
61 charset = part.get_content_charset()
62 payload_b = part.get_payload(decode=True)
63
64 if payload_b == None:
65 continue
66
67 if descrip_p == None:
68 decript = ""
69 else:
70 descript = descrip_p[0][0]
71
72 if charset == None:
73 charset = 'utf-8'
74
75 payload = payload_b.decode(charset)
76
77 if conttype == "application/pgp-encrypted":
78 if descript == 'PGP/MIME version identification':
79 if payload.strip() != "Version: 1":
80 print("*** Warning... unknown pgp/mime version: " \
81 + payload.strip())
82 continue
83
84 if (filename == "encrypted.asc") or (conttype == "pgp/mime"):
85 dec_msg = decrypt_payload(payload)
86 print_decrypted(dec_msg)
87
88 elif conttype == "text/plain":
89 print(payload)
90
91 else:
92 print(payload)
93
94
95def print_decrypted (message):
96
97 if message == None:
98 return
99
100 for chunk in message:
101 msg = email.parser.Parser().parsestr(chunk[0])
102 sigs = chunk[1]
103
104 msg_walk(msg)
105 for sig in sigs:
106 print_sig(sig)
107
108def print_sig (sig):
109
110 if sig.summary != 0:
111 print("bad sig: " + str(sig.summary))
112 #continue
113
114 fpr = sig.fpr
115 utc = time.gmtime(sig.timestamp)
116
117 g = gpgme.Context()
118 key = g.get_key(fpr)
119
120 name = key.uids[0].name
121 e_addr = key.uids[0].email
122 comment = key.uids[0].comment
123
124 date = "{0:0>4}-{1:0>2}-{2:0>2} {3}:{4}:{5} UTC".format(*utc)
125
126 print("sig from: " + name + " (" + comment + ") <" \
127 + e_addr + ">")
128 print("fingerprint: " + fpr)
129 print("date: " + date)
130
131
132def decrypt_payload (payload):
133
134 blocks = split_message(payload)
135 message = decrypt_blocks(blocks)
136
137 return message
138
139
140def split_message (text):
141
142 pgp_matches = re.search( \
143 '(-----BEGIN PGP MESSAGE-----' \
144 '.*' \
145 '-----END PGP MESSAGE-----)', \
146 text, \
147 re.DOTALL)
148
149 if pgp_matches == None:
150 return None
151 else:
152 return pgp_matches.groups()
153
154
155def decrypt_blocks (blocks):
156
157 if blocks == None:
158 return None
159
160 message = []
161 for block in blocks:
162 plain, sigs = decrypt_block(block)
163
164 message = message + [(plain, sigs)]
165
166 return message
167
168
169def decrypt_block (block):
170
171 block_b = io.BytesIO(block.encode('ASCII'))
172 plain_b = io.BytesIO()
173
174 g = gpgme.Context()
175 sigs = g.decrypt_verify(block_b, plain_b)
176
177 plain = plain_b.getvalue().decode('ASCII')
178 return (plain, sigs)
179
180
181main()
182